@cosmara-ai/community-sdk
Version:
COSMARA Community SDK - Multi-provider AI client with intelligent routing and 1,000 free requests/month
1,540 lines (1,532 loc) • 54.8 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
AIError: () => AIError,
AIMarketplaceCommunityClient: () => AIMarketplaceCommunityClient,
APIProvider: () => APIProvider,
AnthropicProvider: () => AnthropicProvider,
COMMUNITY_LIMITS: () => COMMUNITY_LIMITS,
COST_THRESHOLDS: () => COST_THRESHOLDS,
CommunityLicenseValidator: () => CommunityLicenseValidator,
CommunityUsageTracker: () => CommunityUsageTracker,
EDITION: () => EDITION,
GoogleProvider: () => GoogleProvider,
MODEL_EQUIVALENTS: () => MODEL_EQUIVALENTS,
OpenAIProvider: () => OpenAIProvider,
PERFORMANCE_TARGETS: () => PERFORMANCE_TARGETS,
UPGRADE_INFO: () => UPGRADE_INFO,
UPGRADE_MESSAGES: () => UPGRADE_MESSAGES,
VERSION: () => VERSION,
createClient: () => createClient
});
module.exports = __toCommonJS(index_exports);
// src/types.ts
var APIProvider = /* @__PURE__ */ ((APIProvider2) => {
APIProvider2["OPENAI"] = "OPENAI";
APIProvider2["ANTHROPIC"] = "ANTHROPIC";
APIProvider2["GOOGLE"] = "GOOGLE";
return APIProvider2;
})(APIProvider || {});
var AIError = class extends Error {
constructor(config) {
super(config.message);
this.name = "AIError";
this.code = config.code;
this.type = config.type;
this.provider = config.provider;
this.retryable = config.retryable;
this.details = config.details || {};
}
};
var MODEL_EQUIVALENTS = {
"chat-small": {
["OPENAI" /* OPENAI */]: "gpt-3.5-turbo",
["ANTHROPIC" /* ANTHROPIC */]: "claude-3-haiku-20240307",
["GOOGLE" /* GOOGLE */]: "gemini-1.5-flash"
},
"chat-medium": {
["OPENAI" /* OPENAI */]: "gpt-4",
["ANTHROPIC" /* ANTHROPIC */]: "claude-sonnet-4-20250514",
["GOOGLE" /* GOOGLE */]: "gemini-1.5-pro"
},
"chat-large": {
["OPENAI" /* OPENAI */]: "gpt-4-turbo",
["ANTHROPIC" /* ANTHROPIC */]: "claude-3-opus-20240229",
["GOOGLE" /* GOOGLE */]: "gemini-1.5-pro"
}
};
var COMMUNITY_LIMITS = {
requestsPerMonth: 1e3,
requestsPerDay: 100,
requestsPerMinute: 10,
maxUniqueUsers: 10,
commercialUseDetection: true
};
var PERFORMANCE_TARGETS = {
MAX_RESPONSE_TIME: 3e4,
// 30 seconds for community
MAX_STREAM_FIRST_TOKEN: 5e3
// 5 seconds for first token
};
var COST_THRESHOLDS = {
CHEAP_REQUEST: 1e-3,
EXPENSIVE_REQUEST: 0.1
};
var UPGRADE_MESSAGES = {
RATE_LIMIT: "\u26A1 You've reached your Community Edition limits. Upgrade to Developer tier for 50x more requests and ML-powered optimization!",
COMMERCIAL_USE: "\u{1F3E2} Commercial usage detected. Upgrade to Developer tier to unlock commercial licensing and priority support!",
FEATURE_LOCKED: "\u{1F512} This feature requires Developer tier. Upgrade for ML routing, analytics, and cost optimization!",
UPGRADE_CTA: "\u{1F680} Ready to unlock the full potential? Upgrade at https://ai-marketplace.dev/pricing"
};
// src/providers/openai.ts
var OpenAIProvider = class {
constructor(baseUrl) {
this.provider = "OPENAI" /* OPENAI */;
this.defaultHeaders = {
"Content-Type": "application/json",
"User-Agent": "AI-Marketplace-Community-SDK/1.0"
};
this.models = [
{
id: "gpt-3.5-turbo",
provider: "OPENAI" /* OPENAI */,
name: "gpt-3.5-turbo",
displayName: "GPT-3.5 Turbo",
description: "Fast, cost-effective model for most conversational tasks",
maxTokens: 4096,
inputCostPer1K: 15e-4,
outputCostPer1K: 2e-3,
supportsStreaming: true,
supportsTools: true,
contextWindow: 16385,
isActive: true
},
{
id: "gpt-4",
provider: "OPENAI" /* OPENAI */,
name: "gpt-4",
displayName: "GPT-4",
description: "Most capable model for complex reasoning tasks",
maxTokens: 8192,
inputCostPer1K: 0.03,
outputCostPer1K: 0.06,
supportsStreaming: true,
supportsTools: true,
contextWindow: 8192,
isActive: true
},
{
id: "gpt-4o-mini",
provider: "OPENAI" /* OPENAI */,
name: "gpt-4o-mini",
displayName: "GPT-4 Omni Mini",
description: "Efficient and cost-effective model for simple tasks",
maxTokens: 16384,
inputCostPer1K: 15e-5,
outputCostPer1K: 6e-4,
supportsStreaming: true,
supportsTools: true,
contextWindow: 128e3,
isActive: true
}
];
this.baseUrl = baseUrl || "https://api.openai.com/v1";
}
async getModels() {
return this.models.filter((model) => model.isActive);
}
async getModel(modelId) {
return this.models.find((model) => model.id === modelId) || null;
}
async validateApiKey(apiKey) {
try {
const response = await fetch(`${this.baseUrl}/models`, {
method: "GET",
headers: {
...this.defaultHeaders,
"Authorization": `Bearer ${apiKey}`
}
});
return response.ok;
} catch (error) {
return false;
}
}
async estimateCost(request) {
const model = await this.getModel(request.model);
if (!model) {
throw new AIError({
code: "MODEL_NOT_FOUND",
message: `Model ${request.model} not found`,
type: "invalid_request",
provider: this.provider,
retryable: false
});
}
const inputText = request.messages.map((m) => m.content).join(" ");
const estimatedInputTokens = Math.ceil(inputText.length / 4);
const estimatedOutputTokens = request.maxTokens || Math.min(1e3, model.maxTokens * 0.1);
const inputCost = estimatedInputTokens / 1e3 * model.inputCostPer1K;
const outputCost = estimatedOutputTokens / 1e3 * model.outputCostPer1K;
return inputCost + outputCost;
}
async chat(request, apiKey) {
const openaiRequest = this.transformRequest(request);
const response = await fetch(`${this.baseUrl}/chat/completions`, {
method: "POST",
headers: {
...this.defaultHeaders,
"Authorization": `Bearer ${apiKey}`
},
body: JSON.stringify(openaiRequest)
});
if (!response.ok) {
throw await this.handleHttpError(response);
}
const data = await response.json();
return this.transformResponse(data, request);
}
async *chatStream(request, apiKey) {
const openaiRequest = this.transformRequest(request, true);
const response = await fetch(`${this.baseUrl}/chat/completions`, {
method: "POST",
headers: {
...this.defaultHeaders,
"Authorization": `Bearer ${apiKey}`
},
body: JSON.stringify(openaiRequest)
});
if (!response.ok) {
throw await this.handleHttpError(response);
}
if (!response.body) {
throw new AIError({
code: "NO_RESPONSE_BODY",
message: "No response body received for streaming request",
type: "api_error",
provider: this.provider,
retryable: false
});
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
const trimmed = line.trim();
if (trimmed === "" || trimmed === "data: [DONE]") continue;
if (trimmed.startsWith("data: ")) {
try {
const data = JSON.parse(trimmed.slice(6));
const chunk = this.transformStreamChunk(data, request);
if (chunk) yield chunk;
} catch (e) {
continue;
}
}
}
}
} finally {
reader.releaseLock();
}
}
transformRequest(request, stream = false) {
const openaiRequest = {
model: request.model,
messages: request.messages.map((msg) => ({
role: msg.role,
content: msg.content,
...msg.name && { name: msg.name }
})),
stream
};
if (request.maxTokens !== void 0) {
openaiRequest.max_tokens = request.maxTokens;
}
if (request.temperature !== void 0) {
openaiRequest.temperature = request.temperature;
}
if (request.topP !== void 0) {
openaiRequest.top_p = request.topP;
}
if (request.tools) {
openaiRequest.tools = request.tools;
}
if (request.toolChoice) {
if (typeof request.toolChoice === "string") {
openaiRequest.tool_choice = request.toolChoice;
} else {
openaiRequest.tool_choice = {
type: "function",
function: { name: request.toolChoice.name }
};
}
}
return openaiRequest;
}
transformResponse(response, originalRequest) {
const model = this.models.find((m) => m.name === response.model || m.id === response.model);
const usage = this.calculateUsage(response.usage, model);
return {
id: response.id,
model: response.model,
provider: this.provider,
choices: response.choices.map((choice) => ({
index: choice.index,
message: {
role: "assistant",
content: choice.message.content || "",
metadata: originalRequest.metadata || {}
},
finishReason: choice.finish_reason,
toolCalls: choice.message.tool_calls?.map((tc) => ({
id: tc.id,
type: tc.type,
function: tc.function
})) || []
})),
usage,
created: response.created,
metadata: originalRequest.metadata || {}
};
}
transformStreamChunk(chunk, _originalRequest) {
if (!chunk.choices || chunk.choices.length === 0) return null;
return {
id: chunk.id,
model: chunk.model,
provider: this.provider,
choices: chunk.choices.map((choice) => ({
index: choice.index,
delta: {
role: choice.delta.role,
content: choice.delta.content,
toolCalls: choice.delta.tool_calls?.map((tc) => ({
id: tc.id || "",
type: "function",
function: {
name: tc.function?.name || "",
arguments: tc.function?.arguments || ""
}
}))
},
finishReason: choice.finish_reason
})),
usage: chunk.usage ? this.calculateUsage(chunk.usage, this.models.find((m) => m.name === chunk.model)) : {
promptTokens: 0,
completionTokens: 0,
totalTokens: 0,
cost: 0
},
created: chunk.created
};
}
calculateUsage(usage, model) {
const inputCost = model ? usage.prompt_tokens / 1e3 * model.inputCostPer1K : 0;
const outputCost = model ? usage.completion_tokens / 1e3 * model.outputCostPer1K : 0;
return {
promptTokens: usage.prompt_tokens,
completionTokens: usage.completion_tokens,
totalTokens: usage.total_tokens,
cost: inputCost + outputCost
};
}
async handleHttpError(response) {
let errorData = {};
try {
errorData = await response.json();
} catch {
}
const baseError = {
provider: this.provider,
retryable: response.status >= 500 || response.status === 429,
details: {
status: response.status,
statusText: response.statusText,
...errorData
}
};
switch (response.status) {
case 401:
return new AIError({
...baseError,
code: "AUTHENTICATION_ERROR",
message: "Invalid OpenAI API key or authentication failed",
type: "authentication",
retryable: false
});
case 429:
return new AIError({
...baseError,
code: "RATE_LIMIT_ERROR",
message: "OpenAI rate limit exceeded",
type: "rate_limit",
retryable: true
});
case 400:
return new AIError({
...baseError,
code: "INVALID_REQUEST",
message: errorData.error?.message || "Invalid request parameters",
type: "invalid_request",
retryable: false
});
default:
return new AIError({
...baseError,
code: "HTTP_ERROR",
message: `HTTP ${response.status}: ${response.statusText}`,
type: "api_error",
retryable: response.status >= 500
});
}
}
};
// src/providers/anthropic.ts
var AnthropicProvider = class {
constructor(baseUrl) {
this.provider = "ANTHROPIC" /* ANTHROPIC */;
this.defaultHeaders = {
"Content-Type": "application/json",
"User-Agent": "AI-Marketplace-Community-SDK/1.0",
"anthropic-version": "2023-06-01"
};
this.models = [
{
id: "claude-3-haiku-20240307",
provider: "ANTHROPIC" /* ANTHROPIC */,
name: "claude-3-haiku-20240307",
displayName: "Claude 3 Haiku",
description: "Fast and cost-effective model for simple tasks",
maxTokens: 4096,
inputCostPer1K: 25e-5,
outputCostPer1K: 125e-5,
supportsStreaming: true,
supportsTools: false,
contextWindow: 2e5,
isActive: true
},
{
id: "claude-sonnet-4-20250514",
provider: "ANTHROPIC" /* ANTHROPIC */,
name: "claude-sonnet-4-20250514",
displayName: "Claude Sonnet 4",
description: "Balanced performance for most conversational tasks",
maxTokens: 8192,
inputCostPer1K: 3e-3,
outputCostPer1K: 0.015,
supportsStreaming: true,
supportsTools: true,
contextWindow: 2e5,
isActive: true
},
{
id: "claude-3-opus-20240229",
provider: "ANTHROPIC" /* ANTHROPIC */,
name: "claude-3-opus-20240229",
displayName: "Claude 3 Opus",
description: "Most capable model for complex reasoning and analysis",
maxTokens: 4096,
inputCostPer1K: 0.015,
outputCostPer1K: 0.075,
supportsStreaming: true,
supportsTools: true,
contextWindow: 2e5,
isActive: true
}
];
this.baseUrl = baseUrl || "https://api.anthropic.com/v1";
}
async getModels() {
return this.models.filter((model) => model.isActive);
}
async getModel(modelId) {
return this.models.find((model) => model.id === modelId) || null;
}
async validateApiKey(apiKey) {
try {
const response = await fetch(`${this.baseUrl}/messages`, {
method: "POST",
headers: {
...this.defaultHeaders,
"x-api-key": apiKey
},
body: JSON.stringify({
model: "claude-3-haiku-20240307",
max_tokens: 1,
messages: [{ role: "user", content: "Hi" }]
})
});
return response.status !== 401;
} catch (error) {
return false;
}
}
async estimateCost(request) {
const model = await this.getModel(request.model);
if (!model) {
throw new AIError({
code: "MODEL_NOT_FOUND",
message: `Model ${request.model} not found`,
type: "invalid_request",
provider: this.provider,
retryable: false
});
}
const inputText = request.messages.map((m) => m.content).join(" ");
const estimatedInputTokens = Math.ceil(inputText.length / 4);
const estimatedOutputTokens = request.maxTokens || Math.min(1e3, model.maxTokens * 0.1);
const inputCost = estimatedInputTokens / 1e3 * model.inputCostPer1K;
const outputCost = estimatedOutputTokens / 1e3 * model.outputCostPer1K;
return inputCost + outputCost;
}
async chat(request, apiKey) {
const anthropicRequest = this.transformRequest(request);
const response = await fetch(`${this.baseUrl}/messages`, {
method: "POST",
headers: {
...this.defaultHeaders,
"x-api-key": apiKey
},
body: JSON.stringify(anthropicRequest)
});
if (!response.ok) {
throw await this.handleHttpError(response);
}
const data = await response.json();
return this.transformResponse(data, request);
}
async *chatStream(request, apiKey) {
const anthropicRequest = this.transformRequest(request, true);
const response = await fetch(`${this.baseUrl}/messages`, {
method: "POST",
headers: {
...this.defaultHeaders,
"x-api-key": apiKey
},
body: JSON.stringify(anthropicRequest)
});
if (!response.ok) {
throw await this.handleHttpError(response);
}
if (!response.body) {
throw new AIError({
code: "NO_RESPONSE_BODY",
message: "No response body received for streaming request",
type: "api_error",
provider: this.provider,
retryable: false
});
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
const trimmed = line.trim();
if (trimmed === "" || !trimmed.startsWith("data: ")) continue;
try {
const data = JSON.parse(trimmed.slice(6));
const chunk = this.transformStreamChunk(data, request);
if (chunk) yield chunk;
} catch (e) {
continue;
}
}
}
} finally {
reader.releaseLock();
}
}
transformRequest(request, stream = false) {
const systemMessage = request.messages.find((msg) => msg.role === "system");
const userMessages = request.messages.filter((msg) => msg.role !== "system");
const anthropicRequest = {
model: request.model,
messages: userMessages.map((msg) => ({
role: msg.role,
content: msg.content
})),
max_tokens: request.maxTokens || 1024,
stream
};
if (systemMessage) {
anthropicRequest.system = systemMessage.content;
}
if (request.temperature !== void 0) {
anthropicRequest.temperature = request.temperature;
}
if (request.topP !== void 0) {
anthropicRequest.top_p = request.topP;
}
return anthropicRequest;
}
transformResponse(response, originalRequest) {
const model = this.models.find((m) => m.name === response.model || m.id === response.model);
const usage = this.calculateUsage(response.usage, model);
return {
id: response.id,
model: response.model,
provider: this.provider,
choices: [{
index: 0,
message: {
role: "assistant",
content: response.content[0]?.text || "",
metadata: originalRequest.metadata || {}
},
finishReason: this.mapFinishReason(response.stop_reason)
}],
usage,
created: Date.now(),
metadata: originalRequest.metadata
};
}
transformStreamChunk(chunk, originalRequest) {
if (chunk.type === "content_block_delta" && chunk.delta?.text) {
return {
id: chunk.id || `chunk_${Date.now()}`,
model: originalRequest.model,
provider: this.provider,
choices: [{
index: 0,
delta: {
role: "assistant",
content: chunk.delta.text
},
finishReason: null
}],
created: Date.now()
};
}
if (chunk.type === "message_stop") {
return {
id: chunk.id || `chunk_${Date.now()}`,
model: originalRequest.model,
provider: this.provider,
choices: [{
index: 0,
delta: {},
finishReason: "stop"
}],
created: Date.now()
};
}
return null;
}
calculateUsage(usage, model) {
const inputCost = model ? usage.input_tokens / 1e3 * model.inputCostPer1K : 0;
const outputCost = model ? usage.output_tokens / 1e3 * model.outputCostPer1K : 0;
return {
promptTokens: usage.input_tokens,
completionTokens: usage.output_tokens,
totalTokens: usage.input_tokens + usage.output_tokens,
cost: inputCost + outputCost
};
}
mapFinishReason(reason) {
switch (reason) {
case "end_turn":
return "stop";
case "max_tokens":
return "length";
default:
return null;
}
}
async handleHttpError(response) {
let errorData = {};
try {
errorData = await response.json();
} catch {
}
const baseError = {
provider: this.provider,
retryable: response.status >= 500 || response.status === 429,
details: {
status: response.status,
statusText: response.statusText,
...errorData
}
};
switch (response.status) {
case 401:
return new AIError({
...baseError,
code: "AUTHENTICATION_ERROR",
message: "Invalid Anthropic API key or authentication failed",
type: "authentication",
retryable: false
});
case 429:
return new AIError({
...baseError,
code: "RATE_LIMIT_ERROR",
message: "Anthropic rate limit exceeded",
type: "rate_limit",
retryable: true
});
case 400:
return new AIError({
...baseError,
code: "INVALID_REQUEST",
message: errorData.error?.message || "Invalid request parameters",
type: "invalid_request",
retryable: false
});
default:
return new AIError({
...baseError,
code: "HTTP_ERROR",
message: `HTTP ${response.status}: ${response.statusText}`,
type: "api_error",
retryable: response.status >= 500
});
}
}
};
// src/providers/google.ts
var GoogleProvider = class {
constructor(baseUrl) {
this.provider = "GOOGLE" /* GOOGLE */;
this.defaultHeaders = {
"Content-Type": "application/json",
"User-Agent": "AI-Marketplace-Community-SDK/1.0"
};
this.models = [
{
id: "gemini-1.5-flash",
provider: "GOOGLE" /* GOOGLE */,
name: "gemini-1.5-flash",
displayName: "Gemini 1.5 Flash",
description: "Fast and efficient model for most tasks",
maxTokens: 8192,
inputCostPer1K: 15e-5,
outputCostPer1K: 6e-4,
supportsStreaming: true,
supportsTools: false,
contextWindow: 1e6,
isActive: true
},
{
id: "gemini-1.5-pro",
provider: "GOOGLE" /* GOOGLE */,
name: "gemini-1.5-pro",
displayName: "Gemini 1.5 Pro",
description: "High-performance model for complex reasoning tasks",
maxTokens: 8192,
inputCostPer1K: 35e-4,
outputCostPer1K: 0.0105,
supportsStreaming: true,
supportsTools: true,
contextWindow: 2e6,
isActive: true
}
];
this.baseUrl = baseUrl || "https://generativelanguage.googleapis.com/v1beta";
}
async getModels() {
return this.models.filter((model) => model.isActive);
}
async getModel(modelId) {
return this.models.find((model) => model.id === modelId) || null;
}
async validateApiKey(apiKey) {
try {
const response = await fetch(`${this.baseUrl}/models?key=${apiKey}`, {
method: "GET",
headers: this.defaultHeaders
});
return response.ok;
} catch (error) {
return false;
}
}
async estimateCost(request) {
const model = await this.getModel(request.model);
if (!model) {
throw new AIError({
code: "MODEL_NOT_FOUND",
message: `Model ${request.model} not found`,
type: "invalid_request",
provider: this.provider,
retryable: false
});
}
const inputText = request.messages.map((m) => m.content).join(" ");
const estimatedInputTokens = Math.ceil(inputText.length / 4);
const estimatedOutputTokens = request.maxTokens || Math.min(1e3, model.maxTokens * 0.1);
const inputCost = estimatedInputTokens / 1e3 * model.inputCostPer1K;
const outputCost = estimatedOutputTokens / 1e3 * model.outputCostPer1K;
return inputCost + outputCost;
}
async chat(request, apiKey) {
const googleRequest = this.transformRequest(request);
const url = `${this.baseUrl}/models/${request.model}:generateContent?key=${apiKey}`;
const response = await fetch(url, {
method: "POST",
headers: this.defaultHeaders,
body: JSON.stringify(googleRequest)
});
if (!response.ok) {
throw await this.handleHttpError(response);
}
const data = await response.json();
return this.transformResponse(data, request);
}
async *chatStream(request, apiKey) {
const googleRequest = this.transformRequest(request);
const url = `${this.baseUrl}/models/${request.model}:streamGenerateContent?key=${apiKey}`;
const response = await fetch(url, {
method: "POST",
headers: this.defaultHeaders,
body: JSON.stringify(googleRequest)
});
if (!response.ok) {
throw await this.handleHttpError(response);
}
if (!response.body) {
throw new AIError({
code: "NO_RESPONSE_BODY",
message: "No response body received for streaming request",
type: "api_error",
provider: this.provider,
retryable: false
});
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
const trimmed = line.trim();
if (trimmed === "" || trimmed === "[" || trimmed === "]" || trimmed === ",") continue;
try {
const data = JSON.parse(trimmed);
const chunk = this.transformStreamChunk(data, request);
if (chunk) yield chunk;
} catch (e) {
continue;
}
}
}
} finally {
reader.releaseLock();
}
}
transformRequest(request) {
const systemMessage = request.messages.find((msg) => msg.role === "system");
const userMessages = request.messages.filter((msg) => msg.role !== "system");
const googleRequest = {
contents: userMessages.map((msg) => ({
role: msg.role === "assistant" ? "model" : "user",
parts: [{ text: msg.content }]
}))
};
if (systemMessage) {
googleRequest.systemInstruction = {
parts: [{ text: systemMessage.content }]
};
}
if (request.temperature !== void 0 || request.topP !== void 0 || request.maxTokens !== void 0) {
googleRequest.generationConfig = {};
if (request.temperature !== void 0) {
googleRequest.generationConfig.temperature = request.temperature;
}
if (request.topP !== void 0) {
googleRequest.generationConfig.topP = request.topP;
}
if (request.maxTokens !== void 0) {
googleRequest.generationConfig.maxOutputTokens = request.maxTokens;
}
}
return googleRequest;
}
transformResponse(response, originalRequest) {
const model = this.models.find((m) => m.name === originalRequest.model || m.id === originalRequest.model);
const usage = this.calculateUsage(response.usageMetadata, model);
return {
id: `google_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
model: originalRequest.model,
provider: this.provider,
choices: response.candidates.map((candidate) => ({
index: candidate.index,
message: {
role: "assistant",
content: candidate.content.parts[0]?.text || "",
metadata: originalRequest.metadata
},
finishReason: this.mapFinishReason(candidate.finishReason)
})),
usage,
created: Date.now(),
metadata: originalRequest.metadata
};
}
transformStreamChunk(chunk, originalRequest) {
if (!chunk.candidates || chunk.candidates.length === 0) return null;
const candidate = chunk.candidates[0];
if (!candidate.content?.parts?.[0]?.text) return null;
return {
id: `google_chunk_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
model: originalRequest.model,
provider: this.provider,
choices: [{
index: 0,
delta: {
role: "assistant",
content: candidate.content.parts[0].text
},
finishReason: candidate.finishReason === "STOP" ? "stop" : null
}],
usage: chunk.usageMetadata ? this.calculateUsage(chunk.usageMetadata, this.models.find((m) => m.id === originalRequest.model)) : void 0,
created: Date.now()
};
}
calculateUsage(usage, model) {
const inputCost = model ? usage.promptTokenCount / 1e3 * model.inputCostPer1K : 0;
const outputCost = model ? usage.candidatesTokenCount / 1e3 * model.outputCostPer1K : 0;
return {
promptTokens: usage.promptTokenCount,
completionTokens: usage.candidatesTokenCount,
totalTokens: usage.totalTokenCount,
cost: inputCost + outputCost
};
}
mapFinishReason(reason) {
switch (reason) {
case "STOP":
return "stop";
case "MAX_TOKENS":
return "length";
case "SAFETY":
case "RECITATION":
return "content_filter";
default:
return null;
}
}
async handleHttpError(response) {
let errorData = {};
try {
errorData = await response.json();
} catch {
}
const baseError = {
provider: this.provider,
retryable: response.status >= 500 || response.status === 429,
details: {
status: response.status,
statusText: response.statusText,
...errorData
}
};
switch (response.status) {
case 401:
case 403:
return new AIError({
...baseError,
code: "AUTHENTICATION_ERROR",
message: "Invalid Google API key or authentication failed",
type: "authentication",
retryable: false
});
case 429:
return new AIError({
...baseError,
code: "RATE_LIMIT_ERROR",
message: "Google AI rate limit exceeded",
type: "rate_limit",
retryable: true
});
case 400:
return new AIError({
...baseError,
code: "INVALID_REQUEST",
message: errorData.error?.message || "Invalid request parameters",
type: "invalid_request",
retryable: false
});
default:
return new AIError({
...baseError,
code: "HTTP_ERROR",
message: `HTTP ${response.status}: ${response.statusText}`,
type: "api_error",
retryable: response.status >= 500
});
}
}
};
// src/license.ts
var CommunityUsageTracker = class {
constructor(limits = COMMUNITY_LIMITS, userId) {
this.usageHistory = [];
this.storageKey = "ai-marketplace-community-usage";
this.limits = limits;
this.userFingerprint = userId || this.generateFingerprint();
this.loadUsageHistory();
this.startCleanup();
}
/**
* Check if current request would exceed usage limits
*/
async checkLimits() {
const now = Date.now();
const oneMinute = 60 * 1e3;
const oneDay = 24 * 60 * 60 * 1e3;
const oneMonth = 30 * 24 * 60 * 60 * 1e3;
const recentMinute = this.usageHistory.filter(
(record) => now - record.timestamp < oneMinute
);
if (recentMinute.length >= this.limits.requestsPerMinute) {
throw new AIError({
code: "RATE_LIMIT_MINUTE",
message: `${UPGRADE_MESSAGES.RATE_LIMIT} (${recentMinute.length}/${this.limits.requestsPerMinute} requests per minute)`,
type: "usage_limit",
provider: "OPENAI" /* OPENAI */,
retryable: true,
details: {
limit: this.limits.requestsPerMinute,
current: recentMinute.length,
resetIn: oneMinute - (now - Math.min(...recentMinute.map((r) => r.timestamp))),
upgradeUrl: "https://ai-marketplace.dev/pricing"
}
});
}
const recentDay = this.usageHistory.filter(
(record) => now - record.timestamp < oneDay
);
if (recentDay.length >= this.limits.requestsPerDay) {
throw new AIError({
code: "RATE_LIMIT_DAILY",
message: `${UPGRADE_MESSAGES.RATE_LIMIT} (${recentDay.length}/${this.limits.requestsPerDay} requests per day)`,
type: "usage_limit",
provider: "OPENAI" /* OPENAI */,
retryable: true,
details: {
limit: this.limits.requestsPerDay,
current: recentDay.length,
resetIn: oneDay - (now - Math.min(...recentDay.map((r) => r.timestamp))),
upgradeUrl: "https://ai-marketplace.dev/pricing"
}
});
}
const recentMonth = this.usageHistory.filter(
(record) => now - record.timestamp < oneMonth
);
if (recentMonth.length >= this.limits.requestsPerMonth) {
throw new AIError({
code: "RATE_LIMIT_MONTHLY",
message: `${UPGRADE_MESSAGES.RATE_LIMIT} (${recentMonth.length}/${this.limits.requestsPerMonth} requests per month)`,
type: "usage_limit",
provider: "OPENAI" /* OPENAI */,
retryable: true,
details: {
limit: this.limits.requestsPerMonth,
current: recentMonth.length,
resetIn: oneMonth - (now - Math.min(...recentMonth.map((r) => r.timestamp))),
upgradeUrl: "https://ai-marketplace.dev/pricing"
}
});
}
const uniqueUsers = new Set(this.usageHistory.map((record) => record.userId));
if (uniqueUsers.size >= this.limits.maxUniqueUsers) {
throw new AIError({
code: "USER_LIMIT_EXCEEDED",
message: `${UPGRADE_MESSAGES.RATE_LIMIT} (${uniqueUsers.size}/${this.limits.maxUniqueUsers} unique users)`,
type: "usage_limit",
provider: "OPENAI" /* OPENAI */,
retryable: false,
details: {
limit: this.limits.maxUniqueUsers,
current: uniqueUsers.size,
upgradeUrl: "https://ai-marketplace.dev/pricing"
}
});
}
if (this.limits.commercialUseDetection && this.detectCommercialUsage()) {
throw new AIError({
code: "COMMERCIAL_USE_DETECTED",
message: UPGRADE_MESSAGES.COMMERCIAL_USE,
type: "usage_limit",
provider: "OPENAI" /* OPENAI */,
retryable: false,
details: {
commercialIndicators: this.getCommercialIndicators(),
upgradeUrl: "https://ai-marketplace.dev/pricing",
contactSales: "https://ai-marketplace.dev/contact"
}
});
}
}
/**
* Record usage after successful request
*/
recordUsage(provider, model, tokensUsed, cost, userId) {
const record = {
timestamp: Date.now(),
provider,
model,
tokensUsed,
cost,
userId: userId || this.userFingerprint
};
this.usageHistory.push(record);
this.saveUsageHistory();
}
/**
* Get current usage statistics
*/
getUsageStats() {
const now = Date.now();
const oneMinute = 60 * 1e3;
const oneDay = 24 * 60 * 60 * 1e3;
const oneMonth = 30 * 24 * 60 * 60 * 1e3;
const recentMinute = this.usageHistory.filter((r) => now - r.timestamp < oneMinute);
const recentDay = this.usageHistory.filter((r) => now - r.timestamp < oneDay);
const recentMonth = this.usageHistory.filter((r) => now - r.timestamp < oneMonth);
return {
requestsThisMinute: recentMinute.length,
requestsThisDay: recentDay.length,
requestsThisMonth: recentMonth.length,
uniqueUsers: new Set(this.usageHistory.map((r) => r.userId)).size,
totalCost: this.usageHistory.reduce((sum, r) => sum + r.cost, 0),
commercialIndicators: this.getCommercialIndicators()
};
}
/**
* Check if usage patterns indicate commercial use
*/
detectCommercialUsage() {
const indicators = this.getCommercialIndicators();
const indicatorCount = Object.values(indicators).filter(Boolean).length;
return indicatorCount >= 3;
}
/**
* Analyze usage patterns for commercial indicators
*/
getCommercialIndicators() {
const now = Date.now();
const oneWeek = 7 * 24 * 60 * 60 * 1e3;
const recentWeek = this.usageHistory.filter((r) => now - r.timestamp < oneWeek);
const avgDailyRequests = recentWeek.length / 7;
const highRequestVolume = avgDailyRequests > 20;
const businessHoursRequests = recentWeek.filter((record) => {
const date = new Date(record.timestamp);
const hour = date.getHours();
const dayOfWeek = date.getDay();
return dayOfWeek >= 1 && dayOfWeek <= 5 && hour >= 9 && hour <= 18;
});
const businessHours = businessHoursRequests.length / recentWeek.length > 0.7;
const uniqueUsers = new Set(recentWeek.map((r) => r.userId));
const multipleUniqueUsers = uniqueUsers.size > 3;
const uniqueDays = new Set(recentWeek.map((r) => {
const date = new Date(r.timestamp);
return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
}));
const consistentUsagePattern = uniqueDays.size >= 5;
const providersUsed = new Set(recentWeek.map((r) => r.provider));
const apiKeyPatterns = providersUsed.size >= 2;
return {
highRequestVolume,
businessHours,
multipleUniqueUsers,
consistentUsagePattern,
apiKeyPatterns
};
}
/**
* Generate unique fingerprint for user tracking
*/
generateFingerprint() {
const factors = [
typeof window !== "undefined" ? window.navigator?.userAgent : "node",
typeof window !== "undefined" ? window.screen?.width : "server",
Date.now().toString(36),
Math.random().toString(36).substr(2, 9)
];
return `user_${factors.join("_").replace(/[^a-zA-Z0-9_]/g, "_")}`;
}
/**
* Load usage history from storage
*/
loadUsageHistory() {
try {
if (typeof localStorage !== "undefined") {
const stored = localStorage.getItem(this.storageKey);
if (stored) {
this.usageHistory = JSON.parse(stored);
}
}
} catch (error) {
console.warn("Failed to load usage history:", error);
this.usageHistory = [];
}
}
/**
* Save usage history to storage
*/
saveUsageHistory() {
try {
if (typeof localStorage !== "undefined") {
const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1e3;
const recentHistory = this.usageHistory.filter((r) => r.timestamp > thirtyDaysAgo);
localStorage.setItem(this.storageKey, JSON.stringify(recentHistory));
this.usageHistory = recentHistory;
}
} catch (error) {
console.warn("Failed to save usage history:", error);
}
}
/**
* Start cleanup interval to remove old records
*/
startCleanup() {
setInterval(() => {
const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1e3;
this.usageHistory = this.usageHistory.filter((r) => r.timestamp > thirtyDaysAgo);
this.saveUsageHistory();
}, 60 * 60 * 1e3);
}
};
var CommunityLicenseValidator = class {
/**
* Validate that usage complies with Community Edition license
*/
validateUsage(usageStats) {
const violations = [];
const recommendations = [];
const indicators = usageStats.commercialIndicators;
if (Object.values(indicators).filter(Boolean).length >= 2) {
violations.push("Commercial usage patterns detected");
recommendations.push("Upgrade to Developer tier for commercial licensing");
}
if (usageStats.requestsThisMonth > COMMUNITY_LIMITS.requestsPerMonth * 0.8) {
recommendations.push("Approaching monthly limit - consider upgrading for higher limits");
}
if (usageStats.uniqueUsers > COMMUNITY_LIMITS.maxUniqueUsers * 0.8) {
recommendations.push("Approaching user limit - upgrade for unlimited users");
}
return {
isValid: violations.length === 0,
violations,
recommendations
};
}
/**
* Display upgrade prompt to user
*/
displayUpgradePrompt(reason) {
const messages = {
limits: UPGRADE_MESSAGES.RATE_LIMIT,
commercial: UPGRADE_MESSAGES.COMMERCIAL_USE,
features: UPGRADE_MESSAGES.FEATURE_LOCKED
};
console.warn(`
\u{1F680} AI Marketplace SDK - Upgrade Notice
`);
console.warn(messages[reason]);
console.warn(UPGRADE_MESSAGES.UPGRADE_CTA);
console.warn(`
\u{1F4B0} Developer Tier Benefits:`);
console.warn(` \u2022 50,000 requests/month (50x more than Community)`);
console.warn(` \u2022 ML-powered routing for cost optimization`);
console.warn(` \u2022 Advanced analytics and insights`);
console.warn(` \u2022 Intelligent fallbacks and error handling`);
console.warn(` \u2022 Commercial usage rights`);
console.warn(` \u2022 Priority support
`);
}
};
// src/client.ts
var AIMarketplaceCommunityClient = class {
constructor(config) {
this.providers = {};
this.cache = /* @__PURE__ */ new Map();
if (!config.apiKeys || Object.keys(config.apiKeys).length === 0) {
throw new Error("At least one API key is required (openai, anthropic, or google)");
}
this.apiKeys = config.apiKeys;
this.userId = config.userId || this.generateUserId();
this.enableUsageTracking = config.enableUsageTracking ?? true;
this.usageTracker = new CommunityUsageTracker(void 0, this.userId);
this.licenseValidator = new CommunityLicenseValidator();
this.initializeProviders(config.baseUrls);
this.startCacheCleanup();
this.showWelcomeMessage();
}
/**
* Send a chat completion request (Community Edition)
* Note: No ML routing - manual provider selection only
*/
async chat(request, options = {}) {
if (this.enableUsageTracking) {
await this.usageTracker.checkLimits();
}
const userId = options.userId || this.userId;
const cachedResponse = this.getCachedResponse(request);
if (cachedResponse) {
return cachedResponse;
}
let selectedProvider;
if (options.provider) {
selectedProvider = options.provider;
} else {
selectedProvider = this.getRandomAvailableProvider();
}
const provider = this.providers[selectedProvider];
const apiKey = this.getApiKey(selectedProvider);
if (!provider) {
throw new AIError({
code: "PROVIDER_NOT_AVAILABLE",
message: `Provider ${selectedProvider} is not available. Community Edition supports OpenAI, Anthropic, and Google.`,
type: "invalid_request",
provider: selectedProvider,
retryable: false
});
}
if (!apiKey) {
throw new AIError({
code: "API_KEY_MISSING",
message: `API key for ${selectedProvider} is not provided. Please add it to your configuration.`,
type: "authentication",
provider: selectedProvider,
retryable: false
});
}
try {
const finalRequest = {
...request,
model: request.model || this.getDefaultModelForProvider(selectedProvider)
};
const response = await provider.chat(finalRequest, apiKey);
if (this.enableUsageTracking && response.usage) {
this.usageTracker.recordUsage(
selectedProvider,
finalRequest.model,
response.usage.totalTokens,
response.usage.cost,
userId
);
}
this.setCachedResponse(request, response);
this.maybeShowUpgradePrompt();
return response;
} catch (error) {
if (error instanceof AIError && error.code === "RATE_LIMIT_ERROR") {
this.licenseValidator.displayUpgradePrompt("limits");
}
throw error;
}
}
/**
* Send a streaming chat completion request (Community Edition)
*/
async *chatStream(request, options = {}) {
if (this.enableUsageTracking) {
await this.usageTracker.checkLimits();
}
const userId = options.userId || this.userId;
let selectedProvider;
if (options.provider) {
selectedProvider = options.provider;
} else {
selectedProvider = this.getRandomAvailableProvider();
}
const provider = this.providers[selectedProvider];
const apiKey = this.getApiKey(selectedProvider);
if (!provider || !apiKey) {
throw new AIError({
code: "PROVIDER_NOT_AVAILABLE",
message: `Provider ${selectedProvider} is not available or API key is missing`,
type: "invalid_request",
provider: selectedProvider,
retryable: false
});
}
const finalRequest = {
...request,
model: request.model || this.getDefaultModelForProvider(selectedProvider),
stream: true
};
let totalTokens = 0;
let totalCost = 0;
try {
for await (const chunk of provider.chatStream(finalRequest, apiKey)) {
if (chunk.usage) {
totalTokens = chunk.usage.totalTokens;
totalCost = chunk.usage.cost;
}
yield chunk;
}
if (this.enableUsageTracking && totalTokens > 0) {
this.usageTracker.recordUsage(
selectedProvider,
finalRequest.model,
totalTokens,
totalCost,
userId
);
}
} catch (error) {
throw error;
}
}
/**
* Get available models from specified provider
*/
async getModels(provider) {
if (provider) {
const providerInstance = this.providers[provider];
const apiKey = this.getApiKey(provider);
if (!providerInstance || !apiKey) {
return [];
}
try {
return await providerInstance.getModels();
} catch (error) {
console.error(`Failed to get models from ${provider}:`, error);
return [];
}
}
const allModels = [];
for (const [providerType, providerInstance] of Object.entries(this.providers)) {
const apiKey = this.getApiKey(providerType);
if (apiKey) {
try {
const models = await providerInstance.getModels();
allModels.push(...models);
} catch (error) {
console.error(`Failed to get models from ${providerType}:`, error);
}
}
}
return allModels;
}
/**
* Validate API keys for all providers
*/
async validateApiKeys() {
const results = {};
for (const [provider, providerInstance] of Object.entries(this.providers)) {
const apiKey = this.getApiKey(provider);
if (apiKey) {
try {
results[provider] = await providerInstance.validateApiKey(apiKey);
} catch (error) {
results[provider] = false;
}
} else {
results[provider] = false;
}
}
return results;
}
/**
* Get usage statistics (Community Edition)
*/
getUsageStats() {
if (!this.enableUsageTracking) {
return {
message: "Usage tracking is disabled",
enableInstructions: "Set enableUsageTracking: true in config to track usage"
};
}
const stats = this.usageTracker.getUsageStats();
const validation = this.licenseValidator.validateUsage(stats);
return {
...stats,
limits: {
requestsPerMinute: 10,
requestsPerDay: 100,
requestsPerMonth: 1e3,
maxUniqueUsers: 10
},
validation,
upgradeMessage: validation.violations.length > 0 ? UPGRADE_MESSAGES.COMMERCIAL_USE : void 0
};
}
/**
* Estimate cost for a request (basic estimation only)
*/
async estimateCost(request, provider) {
const estimates = [];
const providersToCheck = provider ? [provider] : this.getAvailableProviders();
for (const providerType of providersToCheck) {
const providerInstance = this.providers[providerType];
const apiKey = this.getApiKey(providerType);
if (providerInstance && apiKey) {
try {
const cost = await providerInstance.estimateCost(request);
estimates.push({ provider: providerType, cost });
} catch (error) {
console.error(`Failed to estimate cost for ${providerType}:`, error);
}
}
}
return estimates.sort((a, b) => a.cost - b.cost);
}
/**
* Clear cache
*/
clearCache() {
this.cache.clear();
}
// Private methods
initializeProviders(baseUrls) {
if (this.apiKeys.openai) {
this.providers["OPENAI" /* OPENAI */] = new OpenAIProvider(baseUrls?.openai);
}
if (this.apiKeys.anthropic) {
this.providers["ANTHROPIC" /* ANTHROPIC */] = new AnthropicProvider(baseUrls?.anthropic);
}
if (this.apiKeys.google) {
this.providers["GOOGLE" /* GOOGLE */] = new GoogleProvider(baseUrls?.google);
}
}
getAvailableProviders() {
return Object.keys(this.providers);
}
getRandomAvailableProvider() {
const available = this.getAvailableProviders();
if (available.length === 0) {
throw new AIError({
code: "NO_PROVIDERS_AVAILABLE",
message: "No API providers are configured. Please provide at least one API key.",