UNPKG

@baseai/core

Version:

The Web AI Framework's core - BaseAI.dev

1,073 lines (1,065 loc) 27.4 kB
// src/helpers/stream.ts import { ChatCompletionStream } from "openai/lib/ChatCompletionStream"; import { Stream } from "openai/streaming"; var fromReadableStream = (readableStream) => { return ChatCompletionStream.fromReadableStream(readableStream); }; var getRunner = (readableStream) => { return fromReadableStream(readableStream); }; var getTextPart = (chunk) => { var _a, _b; return ((_b = (_a = chunk.choices[0]) == null ? void 0 : _a.delta) == null ? void 0 : _b.content) || ""; }; function handleResponseStream({ response, rawResponse }) { const controller = new AbortController(); const streamSSE = Stream.fromSSEResponse(response, controller); const stream = streamSSE.toReadableStream(); const result = { stream, threadId: response.headers.get("lb-thread-id") }; if (rawResponse) { result.rawResponse = { headers: Object.fromEntries(response.headers.entries()) }; } return result; } async function getToolsFromStream(stream) { let run = getRunner(stream); const { choices } = await run.finalChatCompletion(); return choices[0].message.tool_calls; } // src/common/errors.ts var APIError = class _APIError extends Error { constructor(status, error, message, headers) { super(_APIError.makeMessage(status, error, message)); this.status = status; this.headers = headers; this.request_id = headers == null ? void 0 : headers["lb-request-id"]; const data = error; this.error = data; this.code = data == null ? void 0 : data["code"]; this.status = data == null ? void 0 : data["status"]; } static makeMessage(status, error, message) { const msg = (error == null ? void 0 : error.message) ? typeof error.message === "string" ? error.message : JSON.stringify(error.message) : error ? JSON.stringify(error) : message; if (status && msg) { return `${status} ${msg}`; } if (status) { return `${status} status code (no body)`; } if (msg) { return msg; } return "(no status code or body)"; } static generate(status, errorResponse, message, headers) { if (!status) { return new APIConnectionError({ cause: errorResponse instanceof Error ? errorResponse : void 0 }); } const error = errorResponse == null ? void 0 : errorResponse["error"]; switch (status) { case 400: return new BadRequestError(status, error, message, headers); case 401: return new AuthenticationError(status, error, message, headers); case 403: return new PermissionDeniedError( status, error, message, headers ); case 404: return new NotFoundError(status, error, message, headers); case 409: return new ConflictError(status, error, message, headers); case 422: return new UnprocessableEntityError( status, error, message, headers ); case 429: return new RateLimitError(status, error, message, headers); default: return status >= 500 ? new InternalServerError(status, error, message, headers) : new _APIError(status, error, message, headers); } } }; var APIConnectionError = class extends APIError { constructor({ message, cause }) { super(void 0, void 0, message || "Connection error.", void 0); this.status = void 0; if (cause) this.cause = cause; } }; var BadRequestError = class extends APIError { constructor() { super(...arguments); this.status = 400; } }; var AuthenticationError = class extends APIError { constructor() { super(...arguments); this.status = 401; } }; var PermissionDeniedError = class extends APIError { constructor() { super(...arguments); this.status = 403; } }; var NotFoundError = class extends APIError { constructor() { super(...arguments); this.status = 404; } }; var ConflictError = class extends APIError { constructor() { super(...arguments); this.status = 409; } }; var UnprocessableEntityError = class extends APIError { constructor() { super(...arguments); this.status = 422; } }; var RateLimitError = class extends APIError { constructor() { super(...arguments); this.status = 429; } }; var InternalServerError = class extends APIError { }; // src/common/request.ts var Request = class { constructor(config) { this.config = config; } async send({ endpoint, ...options }) { var _a, _b, _c, _d; const url = this.buildUrl({ endpoint }); const headers = this.buildHeaders({ headers: options.headers }); let response; try { response = await this.makeRequest({ url, options: { ...options, endpoint }, headers }); } catch (error) { throw new APIConnectionError({ cause: error instanceof Error ? error : void 0 }); } if (!response.ok) { await this.handleErrorResponse({ response }); } const threadId = response.headers.get("lb-thread-id"); if ((_a = options.body) == null ? void 0 : _a.stream) { return handleResponseStream({ response, rawResponse: options.body.rawResponse }); } return this.handleRunResponse({ response, isChat: (_b = options.body) == null ? void 0 : _b.chat, threadId, rawResponse: (_d = (_c = options.body) == null ? void 0 : _c.rawResponse) != null ? _d : false }); } buildUrl({ endpoint }) { return `${this.config.baseUrl}${endpoint}`; } buildHeaders({ headers }) { var _a; return { "Content-Type": "application/json", Authorization: `Bearer ${this.config.apiKey}`, "LB-LLM-Key": (_a = this.config.llmKey) != null ? _a : "", ...headers }; } async makeRequest({ url, options, headers }) { const resp = await fetch(url, { method: options.method, headers, body: JSON.stringify(options.body), ...this.config.timeout && { signal: AbortSignal.timeout(this.config.timeout) } }); return resp; } async handleErrorResponse({ response }) { let errorBody; try { errorBody = await response.json(); } catch (e) { errorBody = await response.text(); } throw APIError.generate( response.status, errorBody, response.statusText, Object.fromEntries(response.headers.entries()) ); } async handleRunResponse({ response, isChat, threadId, rawResponse }) { const generateResponse = await response.json(); const buildResponse = generateResponse.raw ? { completion: generateResponse.completion, ...generateResponse.raw } : generateResponse; const result = { ...buildResponse }; result.threadId = threadId; if (rawResponse) { result.rawResponse = { headers: Object.fromEntries(response.headers.entries()) }; } return result; } async post(options) { return this.send({ ...options, method: "POST" }); } async get(options) { return this.send({ ...options, method: "GET" }); } async put(options) { return this.send({ ...options, method: "PUT" }); } async delete(options) { return this.send({ ...options, method: "DELETE" }); } }; // src/data/models.ts var OPEN_AI = "OpenAI"; var ANTHROPIC = "Anthropic"; var TOGETHER_AI = "Together"; var GOOGLE = "Google"; var GROQ = "Groq"; var COHERE = "Cohere"; var FIREWORKS_AI = "Fireworks AI"; var PERPLEXITY = "Perplexity"; var MISTRAL_AI = "Mistral AI"; var OLLAMA = "ollama"; var X_AI = "xAI"; var modelsByProvider = { [OPEN_AI]: [ { id: "gpt-4o", provider: OPEN_AI, promptCost: 5, completionCost: 15 }, { id: "gpt-4o-2024-08-06", provider: OPEN_AI, promptCost: 2.5, completionCost: 10 }, { id: "gpt-4o-mini", provider: OPEN_AI, promptCost: 0.15, completionCost: 0.6 }, { id: "gpt-4-turbo", provider: OPEN_AI, promptCost: 10, completionCost: 30 }, { id: "gpt-4-turbo-preview", provider: OPEN_AI, promptCost: 10, completionCost: 30 }, { id: "gpt-4-0125-preview", provider: OPEN_AI, promptCost: 10, completionCost: 30 }, { id: "gpt-4-1106-preview", provider: OPEN_AI, promptCost: 10, completionCost: 30 }, { id: "gpt-4", provider: OPEN_AI, promptCost: 30, completionCost: 60 }, { id: "gpt-4-0613", provider: OPEN_AI, promptCost: 30, completionCost: 60 }, { id: "gpt-4-32k", provider: OPEN_AI, promptCost: 60, completionCost: 120 }, { id: "gpt-3.5-turbo", provider: OPEN_AI, promptCost: 0.5, completionCost: 1.5 }, { id: "gpt-3.5-turbo-0125", provider: OPEN_AI, promptCost: 0.5, completionCost: 1.5 }, { id: "gpt-3.5-turbo-1106", provider: OPEN_AI, promptCost: 1, completionCost: 2 }, { id: "gpt-3.5-turbo-16k", provider: OPEN_AI, promptCost: 3, completionCost: 4 } ], [TOGETHER_AI]: [ { id: "meta-llama/Llama-3.3-70B-Instruct-Turbo", provider: TOGETHER_AI, promptCost: 0.88, completionCost: 0.88 }, { id: "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", provider: TOGETHER_AI, promptCost: 5, completionCost: 5 }, { id: "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", provider: TOGETHER_AI, promptCost: 0.88, completionCost: 0.88 }, { id: "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo", provider: TOGETHER_AI, promptCost: 0.18, completionCost: 0.18 }, { id: "meta-llama/Llama-3-70b-chat-hf", provider: TOGETHER_AI, promptCost: 0.9, completionCost: 0.9 }, { id: "meta-llama/Llama-3-8b-chat-hf", provider: TOGETHER_AI, promptCost: 0.2, completionCost: 0.2 }, { id: "togethercomputer/Llama-2-7B-32K-Instruct", provider: TOGETHER_AI, promptCost: 0.2, completionCost: 0.2 }, { id: "meta-llama/Llama-2-13b-chat-hf", provider: TOGETHER_AI, promptCost: 0.225, completionCost: 0.225 }, { id: "meta-llama/Llama-2-70b-chat-hf", provider: TOGETHER_AI, promptCost: 0.9, completionCost: 0.9 }, { id: "google/gemma-7b-it", provider: TOGETHER_AI, promptCost: 0.2, completionCost: 0.2 }, { id: "google/gemma-2b-it", provider: TOGETHER_AI, promptCost: 0.1, completionCost: 0.1 }, { id: "mistralai/Mistral-7B-Instruct-v0.1", provider: TOGETHER_AI, promptCost: 0.2, completionCost: 0.2 }, { id: "mistralai/Mistral-7B-Instruct-v0.2", provider: TOGETHER_AI, promptCost: 0.2, completionCost: 0.2 }, { id: "mistralai/Mixtral-8x7B-Instruct-v0.1", provider: TOGETHER_AI, promptCost: 0.6, completionCost: 0.6 }, { id: "mistralai/Mixtral-8x22B-Instruct-v0.1", provider: TOGETHER_AI, promptCost: 1.2, completionCost: 1.2 }, { id: "databricks/dbrx-instruct", provider: TOGETHER_AI, promptCost: 1.2, completionCost: 1.2 } ], [ANTHROPIC]: [ { id: "claude-3-5-sonnet-latest", provider: ANTHROPIC, promptCost: 3, completionCost: 15 }, { id: "claude-3-5-sonnet-20240620", provider: ANTHROPIC, promptCost: 3, completionCost: 15 }, { id: "claude-3-opus-20240229", provider: ANTHROPIC, promptCost: 15, completionCost: 75 }, { id: "claude-3-sonnet-20240229", provider: ANTHROPIC, promptCost: 3, completionCost: 15 }, { id: "claude-3-haiku-20240307", provider: ANTHROPIC, promptCost: 0.25, completionCost: 1.25 }, { id: "claude-3-5-haiku-20241022", provider: ANTHROPIC, promptCost: 1, completionCost: 5 } ], [GROQ]: [ { id: "llama-3.3-70b-versatile", provider: GROQ, promptCost: 0.59, completionCost: 0.79 }, { id: "llama-3.1-70b-versatile", provider: GROQ, promptCost: 0.59, completionCost: 0.79 }, { id: "llama-3.1-8b-instant", provider: GROQ, promptCost: 0.59, completionCost: 0.79 }, { id: "llama3-70b-8192", provider: GROQ, promptCost: 0.59, completionCost: 0.79 }, { id: "llama3-8b-8192", provider: GROQ, promptCost: 0.05, completionCost: 0.1 }, { id: "mixtral-8x7b-32768", provider: GROQ, promptCost: 0.27, completionCost: 0.27 }, { id: "gemma2-9b-it", provider: GROQ, promptCost: 0.2, completionCost: 0.2 }, { id: "gemma-7b-it", provider: GROQ, promptCost: 0.07, completionCost: 0.07 } ], [GOOGLE]: [ { id: "gemini-1.5-pro-latest", provider: GOOGLE, promptCost: 3.5, completionCost: 10.5 }, { id: "gemini-1.5-flash-latest", provider: GOOGLE, promptCost: 0.075, completionCost: 0.3 }, { id: "gemini-1.5-flash-8b-latest", provider: GOOGLE, promptCost: 0.0375, completionCost: 0.15 }, { id: "gemini-pro", provider: GOOGLE, promptCost: 0.5, completionCost: 1.5 } ], [COHERE]: [ { id: "command-r", provider: COHERE, promptCost: 0.5, completionCost: 1.5 }, { id: "command-r-plus", provider: COHERE, promptCost: 3, completionCost: 15 } ], [FIREWORKS_AI]: [ { id: "llama-v3p3-70b-instruct", provider: FIREWORKS_AI, promptCost: 0.88, completionCost: 0.88 }, { id: "llama-v3p1-405b-instruct", provider: FIREWORKS_AI, promptCost: 3, completionCost: 3 }, { id: "llama-v3p1-70b-instruct", provider: FIREWORKS_AI, promptCost: 0.9, completionCost: 0.9 }, { id: "llama-v3p1-8b-instruct", provider: FIREWORKS_AI, promptCost: 0.2, completionCost: 0.2 }, { id: "yi-large", provider: FIREWORKS_AI, promptCost: 3, completionCost: 3 }, { id: "llama-v3-70b-instruct", provider: FIREWORKS_AI, promptCost: 0.9, completionCost: 0.9 } ], [PERPLEXITY]: [ { id: "llama-3.1-sonar-huge-128k-online", provider: PERPLEXITY, promptCost: 5, completionCost: 5, requestCost: 5e-3 }, { id: "llama-3.1-sonar-large-128k-online", provider: PERPLEXITY, promptCost: 1, completionCost: 1, requestCost: 5e-3 }, { id: "llama-3.1-sonar-small-128k-online", provider: PERPLEXITY, promptCost: 0.2, completionCost: 0.2, requestCost: 5e-3 }, { id: "llama-3.1-sonar-large-128k-chat", provider: PERPLEXITY, promptCost: 1, completionCost: 1 }, { id: "llama-3.1-sonar-small-128k-chat", provider: PERPLEXITY, promptCost: 0.2, completionCost: 0.2 } ], [MISTRAL_AI]: [ { id: "mistral-large-latest", provider: MISTRAL_AI, promptCost: 3, completionCost: 9 }, { id: "open-mistral-nemo", provider: MISTRAL_AI, promptCost: 0.3, completionCost: 0.3 }, { id: "codestral-latest", provider: MISTRAL_AI, promptCost: 1, completionCost: 3 } ], [X_AI]: [ { id: "grok-beta", provider: X_AI, promptCost: 5, completionCost: 15 } ] }; // src/utils/get-llm-api-key.ts function getLLMApiKey(modelProvider) { switch (true) { case modelProvider.includes(OPEN_AI): return process.env.OPENAI_API_KEY || ""; case modelProvider === ANTHROPIC: return process.env.ANTHROPIC_API_KEY || ""; case modelProvider === TOGETHER_AI: return process.env.TOGETHER_API_KEY || ""; case modelProvider === GROQ: return process.env.GROQ_API_KEY || ""; case modelProvider === GOOGLE: return process.env.GOOGLE_API_KEY || ""; case modelProvider.includes(COHERE): return process.env.COHERE_API_KEY || ""; case modelProvider.includes(FIREWORKS_AI): return process.env.FIREWORKS_API_KEY || ""; case modelProvider.includes(PERPLEXITY): return process.env.PERPLEXITY_API_KEY || ""; case modelProvider.includes(OLLAMA): return process.env.OLLAMA_API_KEY || ""; case modelProvider.includes(X_AI): return process.env.XAI_API_KEY || ""; default: throw new Error(`Unsupported model provider: ${modelProvider}`); } } // src/utils/is-prod.ts var FORCE_PROD = false; var TEST_PROD_LOCALLY = FORCE_PROD; function isProd() { if (TEST_PROD_LOCALLY) return true; return process.env.NODE_ENV === "production"; } function getApiUrl(prod) { if (prod) return "https://api.langbase.com"; else return "http://localhost:9000"; } // src/utils/local-server-running.ts async function isLocalServerRunning() { try { const prod = isProd(); const endpoint = getApiUrl(prod); const response = await fetch(endpoint, { mode: "no-cors", cache: "no-cache" // Prevents caching of the request }); const portUseError = ` Port 9000 is already in use. Terminate the process running on it. Run "npx baseai@latest dev" in an new terminal to start the dev server. `; if (!response.ok) { console.error(portUseError); return false; } const res = await response.json(); if (!res.success) { console.error(portUseError); return false; } return true; } catch (error) { console.error( ` BaseAI dev server is not running. Please run "npx baseai@latest dev" in a new teriminal to start dev server. ` ); return false; } } // src/utils/get-provider.ts function getProvider(providerString) { const providerMap = { openai: OPEN_AI, anthropic: ANTHROPIC, together: TOGETHER_AI, google: GOOGLE, groq: GROQ, cohere: COHERE, fireworks: FIREWORKS_AI, perplexity: PERPLEXITY, ollama: OLLAMA, xai: X_AI, mistral: MISTRAL_AI }; const provider = providerMap[providerString.toLowerCase()]; if (!provider) { throw new Error(`Unknown provider: ${providerString}`); } return provider; } // src/pipes/pipes.ts var Pipe = class { constructor(options) { var _a; this.prod = (_a = options.prod) != null ? _a : isProd(); this.baseUrl = getApiUrl(this.prod); this.request = new Request({ apiKey: options.apiKey, baseUrl: this.baseUrl }); this.pipe = options; this.entityApiKey = options.apiKey; delete this.pipe.prod; delete this.pipe.apiKey; this.tools = this.getToolsFromPipe(this.pipe); this.maxCalls = options.maxCalls || 100; this.hasTools = Object.keys(this.tools).length > 0; } getToolsFromPipe(pipe) { const tools = {}; if (pipe.tools && Array.isArray(pipe.tools)) { pipe.tools.forEach((tool) => { tools[tool.function.name] = tool.run; }); } return tools; } async runTools(toolCalls) { const toolPromises = toolCalls.map(async (toolCall) => { const toolName = toolCall.function.name; const toolParameters = JSON.parse(toolCall.function.arguments); const toolFunction = this.tools[toolName]; if (!toolFunction) { throw new Error( `Tool ${toolName} not found. If this is intentional, please set runTools to false to disable tool execution by default.` ); } const toolResponse = await toolFunction(toolParameters); return { tool_call_id: toolCall.id, role: "tool", name: toolName, content: JSON.stringify(toolResponse) }; }); return Promise.all(toolPromises); } hasNoToolCalls(message) { return !message.tool_calls || message.tool_calls.length === 0; } getMessagesToSend(messages, responseMessage, toolResults) { return this.prod ? toolResults : [...messages, responseMessage, ...toolResults]; } isStreamRequested(options) { return "stream" in options && options.stream === true; } warnIfToolsWithStream(requestedStream) { if (this.hasTools && requestedStream) { console.warn( "Warning: Streaming is not yet supported in Anthropic models when tools are present in the pipe. Falling back to non-streaming mode." ); } } async handleStreamResponse(options, response) { const endpoint = "/v1/pipes/run"; const stream = this.isStreamRequested(options); const body = { ...options, stream }; const [streamForToolCall, streamForReturn] = response.stream.tee(); const tools = await getToolsFromStream(streamForToolCall); if (tools.length) { let messages = options.messages || []; let currentResponse = { stream: streamForReturn, threadId: response.threadId, rawResponse: response.rawResponse }; let callCount = 0; while (callCount < this.maxCalls) { const [streamForToolCall2, streamForReturn2] = currentResponse.stream.tee(); const tools2 = await getToolsFromStream(streamForToolCall2); if (tools2.length === 0) { return { stream: streamForReturn2, threadId: currentResponse.threadId, rawResponse: response.rawResponse }; } const toolResults = await this.runTools(tools2); const responseMessage = { role: "assistant", content: null, tool_calls: tools2 }; messages = this.getMessagesToSend( messages, responseMessage, toolResults ); currentResponse = await this.createRequest( endpoint, { ...body, messages, threadId: currentResponse.threadId } ); callCount++; } } return { ...response, stream: streamForReturn }; } async run(options) { var _a, _b; const endpoint = "/v1/pipes/run"; const providerString = this.pipe.model.split(":")[0]; const modelProvider = getProvider(providerString); const isAnthropic = modelProvider === ANTHROPIC; const hasTools = this.pipe.tools.length > 0; if (options.name) { this.pipe = { ...this.pipe, name: options.name }; } if (options.apiKey) { this.request = new Request({ apiKey: options.apiKey, baseUrl: this.baseUrl, ...options.llmKey && { llmKey: options.llmKey } || {} }); } if (options.llmKey && !options.apiKey) { this.request = new Request({ apiKey: this.entityApiKey, baseUrl: this.baseUrl, llmKey: options.llmKey }); } let stream = this.isStreamRequested(options); if (isAnthropic && hasTools && stream) { this.warnIfToolsWithStream(stream); stream = false; } let runTools = (_a = options.runTools) != null ? _a : true; if (options.tools && ((_b = options.tools) == null ? void 0 : _b.length)) { runTools = false; } delete options.runTools; const body = { ...options, stream }; let response = await this.createRequest(endpoint, body); if (Object.entries(response).length === 0) { return {}; } if (!runTools) { if (!stream) { return response; } return response; } if (stream) { return await this.handleStreamResponse( options, response ); } let messages = options.messages || []; let currentResponse = response; let callCount = 0; while (callCount < this.maxCalls) { const responseMessage = currentResponse.choices[0].message; if (this.hasNoToolCalls(responseMessage)) { return currentResponse; } const toolResults = await this.runTools( responseMessage.tool_calls ); messages = this.getMessagesToSend( messages, responseMessage, toolResults ); currentResponse = await this.createRequest(endpoint, { ...body, messages, stream: false, threadId: currentResponse.threadId }); callCount++; if (this.hasNoToolCalls(currentResponse.choices[0].message)) { return currentResponse; } } console.warn( `Reached maximum number of calls (${this.maxCalls}). Returning last response.` ); return currentResponse; } async createRequest(endpoint, body) { const isProdEnv = this.prod; const prodOptions = { endpoint, body: { ...body, name: this.pipe.name } }; let localOptions = {}; if (!isProdEnv) { const providerString = this.pipe.model.split(":")[0]; const modelProvider = getProvider(providerString); localOptions = { endpoint, body: { ...body, pipe: this.pipe, llmApiKey: getLLMApiKey(modelProvider) } }; const isServerRunning = await isLocalServerRunning(); if (!isServerRunning) return {}; } return this.request.post(isProdEnv ? prodOptions : localOptions); } }; var generateText = async (options) => { return options.pipe.run(options); }; var streamText = async (options) => { return options.pipe.run({ ...options, stream: true }); }; var processChunk = ({ rawChunk }) => { var _a, _b, _c, _d; if ((_b = (_a = rawChunk.choices[0]) == null ? void 0 : _a.delta) == null ? void 0 : _b.content) { return { type: "content", content: rawChunk.choices[0].delta.content }; } if (((_d = (_c = rawChunk.choices[0]) == null ? void 0 : _c.delta) == null ? void 0 : _d.tool_calls) && rawChunk.choices[0].delta.tool_calls.length > 0) { const toolCall = rawChunk.choices[0].delta.tool_calls[0]; return { type: "toolCall", toolCall }; } return { type: "unknown", rawChunk }; }; var isContent = (chunk) => chunk.type === "content"; var isToolCall = (chunk) => chunk.type === "toolCall"; var isUnknown = (chunk) => chunk.type === "unknown"; var getTextContent = (chunk) => { var _a, _b; return ((_b = (_a = chunk.choices[0]) == null ? void 0 : _a.delta) == null ? void 0 : _b.content) || ""; }; var getTextDelta = (chunk) => { var _a, _b; return ((_b = (_a = chunk.choices[0]) == null ? void 0 : _a.delta) == null ? void 0 : _b.content) || ""; }; var printStreamToStdout = async (runner) => { var _a, _b; for await (const chunk of runner) { const textPart = ((_b = (_a = chunk.choices[0]) == null ? void 0 : _a.delta) == null ? void 0 : _b.content) || ""; process.stdout.write(textPart); } }; export { Pipe, fromReadableStream, generateText, getRunner, getTextContent, getTextDelta, getTextPart, getToolsFromStream, handleResponseStream, isContent, isToolCall, isUnknown, printStreamToStdout, processChunk, streamText }; //# sourceMappingURL=index.mjs.map