vly-integrations
Version:
First-order integrations for AI, email, and payments with automatic usage billing
535 lines (528 loc) • 14.7 kB
JavaScript
'use strict';
var openaiCompatible = require('@ai-sdk/openai-compatible');
var ai = require('ai');
// src/ai/index.ts
var VlyAI = class {
provider;
config;
constructor(config) {
this.config = config;
this.provider = openaiCompatible.createOpenAICompatible({
name: "vly-gateway",
baseURL: "https://ai-gateway.vly.ai/v1/llm",
headers: {
"Authorization": `Bearer ${config.deploymentToken}`
}
});
}
getModel(modelName) {
return this.provider(modelName || "gpt-5");
}
mapMessages(messages) {
return messages.map((msg) => ({
role: msg.role,
content: msg.content
}));
}
async completion(request, _options) {
if (this.config.debug) {
console.log("[Vly] Creating AI completion", { model: request.model });
}
try {
const model = this.getModel(request.model);
const messages = this.mapMessages(request.messages);
const result = await ai.generateText({
model,
messages,
temperature: request.temperature,
maxOutputTokens: request.maxTokens
});
const responseData = {
id: `vly-${Date.now()}`,
choices: [{
message: {
role: "assistant",
content: result.text
},
finishReason: result.finishReason || "stop"
}],
usage: {
promptTokens: result.usage?.inputTokens || 0,
completionTokens: result.usage?.outputTokens || 0,
totalTokens: result.usage?.totalTokens || 0
}
};
if (this.config.debug) {
console.log("[Vly] AI completion successful", {
tokensUsed: responseData.usage.totalTokens
});
}
return {
success: true,
data: responseData
};
} catch (error) {
if (this.config.debug) {
console.error("[Vly] AI completion failed", { error: error.message });
}
return {
success: false,
error: error.message || "Request failed"
};
}
}
async streamCompletion(request, onChunk, _options) {
if (this.config.debug) {
console.log("[Vly] Creating streaming AI completion", { model: request.model });
}
try {
const model = this.getModel(request.model);
const messages = this.mapMessages(request.messages);
const result = await ai.streamText({
model,
messages,
temperature: request.temperature,
maxOutputTokens: request.maxTokens
});
let fullResponse = "";
for await (const delta of result.textStream) {
fullResponse += delta;
onChunk(delta);
}
const usage = await result.usage;
const responseData = {
id: `vly-stream-${Date.now()}`,
choices: [{
message: {
role: "assistant",
content: fullResponse
},
finishReason: "stop"
}],
usage: {
promptTokens: usage?.inputTokens || 0,
completionTokens: usage?.outputTokens || 0,
totalTokens: usage?.totalTokens || 0
}
};
if (this.config.debug) {
console.log("[Vly] Streaming AI completion successful", {
tokensUsed: responseData.usage.totalTokens
});
}
return {
success: true,
data: responseData
};
} catch (error) {
if (this.config.debug) {
console.error("[Vly] Streaming AI completion failed", { error: error.message });
}
return {
success: false,
error: error.message || "Streaming request failed"
};
}
}
async embeddings(input, _options) {
if (this.config.debug) {
console.log("[Vly] Creating embeddings", { inputCount: Array.isArray(input) ? input.length : 1 });
}
return {
success: false,
error: "Embeddings not yet supported with AI SDK OpenAI-compatible provider"
};
}
// Helper method to get the provider for direct AI SDK usage
getProvider() {
return this.provider;
}
};
// src/client.ts
var VlyClient = class {
config;
constructor(config) {
this.config = {
deploymentToken: config.deploymentToken,
debug: config.debug || false
};
}
async request(endpoint, method, data, options) {
const url = `https://ai-gateway.vly.ai${endpoint}`;
const headers = {
"Authorization": `Bearer ${this.config.deploymentToken}`,
"Content-Type": "application/json",
"X-Vly-Version": "0.1.0"
};
const fetchOptions = {
method,
headers,
body: data ? JSON.stringify(data) : void 0
};
const controller = new AbortController();
const timeout = options?.timeout || 3e4;
const timeoutId = setTimeout(() => controller.abort(), timeout);
fetchOptions.signal = controller.signal;
try {
const response = await fetch(url, fetchOptions);
clearTimeout(timeoutId);
const responseData = await response.json().catch(() => ({}));
if (response.ok) {
return {
success: true,
data: responseData,
usage: responseData?.usage
};
} else {
const error = responseData?.error || `Request failed with status ${response.status}`;
if (this.config.debug) {
console.error(`Vly API Error: ${error}`, {
endpoint,
status: response.status,
data: responseData
});
}
return {
success: false,
error
};
}
} catch (error) {
clearTimeout(timeoutId);
const errorMessage = error.name === "AbortError" ? "Request timeout" : error.message || "Unknown error occurred";
if (this.config.debug) {
console.error(`Vly API Request Failed: ${errorMessage}`, {
endpoint,
error
});
}
if (options?.retries && options.retries > 0) {
if (this.config.debug) {
console.log(`Retrying request... (${options.retries} retries left)`);
}
await new Promise((resolve) => setTimeout(resolve, 1e3));
return this.request(endpoint, method, data, {
...options,
retries: options.retries - 1
});
}
return {
success: false,
error: errorMessage
};
}
}
log(message, data) {
if (this.config.debug) {
console.log(`[Vly] ${message}`, data || "");
}
}
};
// src/email/index.ts
var VlyEmail = class extends VlyClient {
async send(email, options) {
this.log("Sending email", {
to: email.to,
subject: email.subject
});
const payload = {
to: Array.isArray(email.to) ? email.to : [email.to],
from: email.from || "noreply@vly.io",
subject: email.subject,
html: email.html,
text: email.text,
attachments: email.attachments,
replyTo: email.replyTo,
cc: email.cc ? Array.isArray(email.cc) ? email.cc : [email.cc] : void 0,
bcc: email.bcc ? Array.isArray(email.bcc) ? email.bcc : [email.bcc] : void 0
};
const response = await this.request(
"/v1/email/send",
"POST",
payload,
options
);
if (response.success) {
this.log("Email sent successfully", {
id: response.data?.id,
status: response.data?.status
});
}
return response;
}
async sendBatch(emails, options) {
this.log("Sending batch emails", { count: emails.length });
const payload = {
emails: emails.map((email) => ({
to: Array.isArray(email.to) ? email.to : [email.to],
from: email.from || "noreply@vly.io",
subject: email.subject,
html: email.html,
text: email.text,
attachments: email.attachments,
replyTo: email.replyTo,
cc: email.cc ? Array.isArray(email.cc) ? email.cc : [email.cc] : void 0,
bcc: email.bcc ? Array.isArray(email.bcc) ? email.bcc : [email.bcc] : void 0
}))
};
return this.request(
"/v1/email/batch",
"POST",
payload,
options
);
}
async getStatus(emailId, options) {
this.log("Getting email status", { emailId });
return this.request(
`/v1/email/status/${emailId}`,
"GET",
void 0,
options
);
}
async verifyDomain(domain, options) {
this.log("Verifying domain", { domain });
return this.request(
"/v1/email/domains/verify",
"POST",
{ domain },
options
);
}
async listDomains(options) {
this.log("Listing email domains");
return this.request(
"/v1/email/domains",
"GET",
void 0,
options
);
}
};
// src/payments/index.ts
var VlyPayments = class extends VlyClient {
async createPaymentIntent(intent, options) {
this.log("Creating payment intent", {
amount: intent.amount,
currency: intent.currency
});
const payload = {
amount: intent.amount,
currency: intent.currency || "usd",
description: intent.description,
metadata: intent.metadata,
customer: intent.customer
};
const response = await this.request(
"/payments/intents",
"POST",
payload,
options
);
if (response.success) {
this.log("Payment intent created", {
id: response.data?.id,
status: response.data?.status
});
}
return response;
}
async confirmPaymentIntent(intentId, paymentMethodId, options) {
this.log("Confirming payment intent", { intentId });
return this.request(
`/payments/intents/${intentId}/confirm`,
"POST",
{ paymentMethodId },
options
);
}
async getPaymentIntent(intentId, options) {
this.log("Getting payment intent", { intentId });
return this.request(
`/payments/intents/${intentId}`,
"GET",
void 0,
options
);
}
async cancelPaymentIntent(intentId, options) {
this.log("Canceling payment intent", { intentId });
return this.request(
`/payments/intents/${intentId}/cancel`,
"POST",
void 0,
options
);
}
async createSubscription(subscription, options) {
this.log("Creating subscription", {
customerId: subscription.customerId,
priceId: subscription.priceId
});
const response = await this.request(
"/payments/subscriptions",
"POST",
subscription,
options
);
if (response.success) {
this.log("Subscription created", {
id: response.data?.id,
status: response.data?.status
});
}
return response;
}
async updateSubscription(subscriptionId, updates, options) {
this.log("Updating subscription", { subscriptionId });
return this.request(
`/payments/subscriptions/${subscriptionId}`,
"PUT",
updates,
options
);
}
async cancelSubscription(subscriptionId, immediately, options) {
this.log("Canceling subscription", {
subscriptionId,
immediately
});
return this.request(
`/payments/subscriptions/${subscriptionId}/cancel`,
"POST",
{ immediately },
options
);
}
async getSubscription(subscriptionId, options) {
this.log("Getting subscription", { subscriptionId });
return this.request(
`/payments/subscriptions/${subscriptionId}`,
"GET",
void 0,
options
);
}
async listSubscriptions(customerId, options) {
this.log("Listing subscriptions", { customerId });
const params = new URLSearchParams();
if (customerId) params.append("customerId", customerId);
if (options?.limit) params.append("limit", options.limit.toString());
if (options?.offset) params.append("offset", options.offset.toString());
const queryString = params.toString();
const endpoint = `/payments/subscriptions${queryString ? `?${queryString}` : ""}`;
return this.request(endpoint, "GET", void 0, options);
}
async createCustomerPortal(session, options) {
this.log("Creating customer portal session", {
customerId: session.customerId
});
const response = await this.request(
"/payments/portal",
"POST",
session,
options
);
if (response.success) {
this.log("Customer portal session created", {
id: response.data?.id,
url: response.data?.url
});
}
return response;
}
async createCheckoutSession(session, options) {
this.log("Creating checkout session", {
mode: session.mode,
lineItems: session.lineItems.length
});
return this.request(
"/payments/checkout",
"POST",
session,
options
);
}
async createCustomer(customer, options) {
this.log("Creating customer", { email: customer.email });
return this.request(
"/payments/customers",
"POST",
customer,
options
);
}
async getCustomer(customerId, options) {
this.log("Getting customer", { customerId });
return this.request(
`/payments/customers/${customerId}`,
"GET",
void 0,
options
);
}
async updateCustomer(customerId, updates, options) {
this.log("Updating customer", { customerId });
return this.request(
`/payments/customers/${customerId}`,
"PUT",
updates,
options
);
}
async listPaymentMethods(customerId, options) {
this.log("Listing payment methods", { customerId });
return this.request(
`/payments/customers/${customerId}/payment-methods`,
"GET",
void 0,
options
);
}
async attachPaymentMethod(paymentMethodId, customerId, options) {
this.log("Attaching payment method", {
paymentMethodId,
customerId
});
return this.request(
`/payments/payment-methods/${paymentMethodId}/attach`,
"POST",
{ customerId },
options
);
}
async detachPaymentMethod(paymentMethodId, options) {
this.log("Detaching payment method", { paymentMethodId });
return this.request(
`/payments/payment-methods/${paymentMethodId}/detach`,
"POST",
void 0,
options
);
}
};
// src/index.ts
var VlyIntegrations = class {
ai;
email;
payments;
constructor(config) {
if (!config.deploymentToken) {
throw new Error("Deployment token is required");
}
this.ai = new VlyAI(config);
this.email = new VlyEmail(config);
this.payments = new VlyPayments(config);
}
};
function createVlyIntegrations(config) {
return new VlyIntegrations(config);
}
exports.VlyAI = VlyAI;
exports.VlyEmail = VlyEmail;
exports.VlyIntegrations = VlyIntegrations;
exports.VlyPayments = VlyPayments;
exports.createVlyIntegrations = createVlyIntegrations;
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map