UNPKG

mcard-js

Version:

MCard - Content-addressable storage with cryptographic hashing, handle resolution, and vector search for Node.js and browsers

403 lines (398 loc) 12.7 kB
import { BaseLLMProvider, DEFAULT_PROVIDER, LLMConfig, LLM_PROVIDERS, OllamaProvider } from "./chunk-QXPHMSPA.js"; import { IO } from "./chunk-MPMRBT5R.js"; import { Either } from "./chunk-2KADE3SE.js"; // src/ptr/llm/providers/WebLLMProvider.ts var WebLLMProvider = class extends BaseLLMProvider { provider_name = "webllm"; config; engine = null; current_model = null; initialization_promise = null; constructor() { super(); this.config = LLM_PROVIDERS["webllm"]; } async _get_engine(model_id) { if (this.engine && this.current_model === model_id) { return Either.right(this.engine); } if (this.initialization_promise) { await this.initialization_promise; if (this.engine && this.current_model === model_id) { return Either.right(this.engine); } } this.initialization_promise = (async () => { try { if (typeof window === "undefined") { throw new Error("WebLLM only supports browser environments."); } let webllm = window.webllm; if (!webllm) { try { webllm = await import("@mlc-ai/web-llm"); } catch (e) { } } if (!webllm) { throw new Error("WebLLM library not found. Please include @mlc-ai/web-llm or add script tag."); } if (!this.engine) { this.engine = await webllm.CreateMLCEngine(model_id, { initProgressCallback: (report) => { console.debug(`[WebLLM] ${report.text}`); } }); } else { await this.engine.reload(model_id); } this.current_model = model_id; } catch (e) { this.engine = null; this.current_model = null; throw e; } })(); try { await this.initialization_promise; return Either.right(this.engine); } catch (e) { this.initialization_promise = null; return Either.left(`WebLLM init failed: ${e.message || e}`); } } async complete(prompt, params) { const model = params.model || this.config.default_model; const engineResult = await this._get_engine(model); if (engineResult.isLeft) return Either.left(engineResult.left); const engine = engineResult.right; try { const completion = await engine.chat.completions.create({ messages: [{ role: "user", content: prompt }], temperature: params.temperature, max_tokens: params.max_tokens, top_p: params.top_p, stream: false }); const content = completion.choices[0]?.message?.content || ""; return Either.right(content); } catch (e) { return Either.left(`WebLLM completion error: ${e.message || e}`); } } async chat(messages, params) { const model = params.model || this.config.default_model; const engineResult = await this._get_engine(model); if (engineResult.isLeft) return Either.left(engineResult.left); const engine = engineResult.right; try { const completion = await engine.chat.completions.create({ messages, temperature: params.temperature, max_tokens: params.max_tokens, top_p: params.top_p, stream: false }); const choice = completion.choices[0]; return Either.right({ content: choice?.message?.content || "", role: choice?.message?.role || "assistant", model, usage: completion.usage }); } catch (e) { return Either.left(`WebLLM chat error: ${e.message || e}`); } } async validate_connection() { if (typeof window === "undefined") return false; if (window.webllm) return true; try { await import("@mlc-ai/web-llm"); return true; } catch { return false; } } async list_models() { return Either.right(this.config.available_models); } }; // src/ptr/llm/providers/MLCLLMProvider.ts import * as http from "http"; import * as https from "https"; var MLCLLMProvider = class extends BaseLLMProvider { provider_name = "mlc-llm"; base_url; timeout; config; constructor(base_url = null, timeout = 120) { super(); this.config = LLM_PROVIDERS["mlc-llm"]; this.base_url = (base_url || this.config.base_url).replace(/\/$/, ""); this.timeout = timeout * 1e3; } async _fetch_json(endpoint, options) { if (typeof globalThis.fetch === "function") { try { const controller = new AbortController(); const id = setTimeout(() => controller.abort(), this.timeout); const response = await fetch(`${this.base_url}${endpoint}`, { ...options, signal: controller.signal }); clearTimeout(id); if (!response.ok) { return Either.left(`HTTP error ${response.status}: ${await response.text()}`); } const data = await response.json(); return Either.right(data); } catch (e) { return Either.left(`Connection error: ${e.message}`); } } return this._node_request(endpoint, options); } _node_request(endpoint, options) { const urlStr = `${this.base_url}${endpoint}`; const url = new URL(urlStr); const isHttps = url.protocol === "https:"; const client = isHttps ? https : http; const reqOptions = { method: options.method || "GET", headers: options.headers || {}, timeout: this.timeout }; return new Promise((resolve) => { const req = client.request(url, reqOptions, (res) => { let body = ""; res.on("data", (chunk) => body += chunk); res.on("end", () => { if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) { try { resolve(Either.right(JSON.parse(body))); } catch (e) { resolve(Either.left(`Parse error: ${e}`)); } } else { resolve(Either.left(`HTTP Error ${res.statusCode}: ${body}`)); } }); }); req.on("error", (e) => resolve(Either.left(e.message))); req.on("timeout", () => { req.destroy(); resolve(Either.left("Request timed out")); }); if (options.body) { req.write(options.body); } req.end(); }); } async complete(prompt, params) { const data = { model: params.model || this.config.default_model, prompt, max_tokens: params.max_tokens, temperature: params.temperature, top_p: params.top_p, stream: false }; const result = await this._fetch_json(this.config.api_path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }); if (result.isLeft) return Either.left(result.left); const response = result.right; if (response.choices && response.choices.length > 0) { return Either.right(response.choices[0].text || ""); } return Either.left(`Unexpected response format: ${JSON.stringify(response)}`); } async chat(messages, params) { const data = { model: params.model || this.config.default_model, messages, max_tokens: params.max_tokens, temperature: params.temperature, top_p: params.top_p, stream: false }; const result = await this._fetch_json(this.config.chat_path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }); if (result.isLeft) return Either.left(result.left); const response = result.right; if (response.choices && response.choices.length > 0) { const message = response.choices[0].message; return Either.right({ content: message.content, role: message.role, model: response.model, usage: response.usage }); } return Either.left(`Unexpected response format: ${JSON.stringify(response)}`); } async validate_connection() { const result = await this._fetch_json(this.config.models_path, { method: "GET" }); return result.isRight; } async list_models() { const result = await this._fetch_json(this.config.models_path, { method: "GET" }); if (result.isLeft) return Either.left(result.left); const response = result.right; if (response.data && Array.isArray(response.data)) { return Either.right(response.data.map((m) => m.id)); } return Either.left("Invalid models response"); } }; // src/ptr/llm/LLMRuntime.ts function get_provider(provider_name = DEFAULT_PROVIDER, base_url = null, timeout = 120) { if (provider_name === "ollama") { return new OllamaProvider(base_url, timeout); } if (provider_name === "webllm") { return new WebLLMProvider(); } if (provider_name === "mlc-llm") { return new MLCLLMProvider(base_url, timeout); } throw new Error(`Unknown provider: ${provider_name}`); } var LLMRuntime = class { provider_name; _provider = null; constructor(provider_name = DEFAULT_PROVIDER) { this.provider_name = provider_name; } get provider() { if (!this._provider) { this._provider = get_provider(this.provider_name); } return this._provider; } async execute(codeOrPath, context, config, chapterDir) { let configCtx = {}; if (typeof context === "object" && context !== null) { configCtx = context; } const concrete = config; const llmConfig = LLMConfig.from_concrete(concrete, configCtx); if (llmConfig.provider !== this.provider_name) { this._provider = get_provider(llmConfig.provider, llmConfig.endpoint_url, llmConfig.timeout); } let prompt = ""; if (typeof context === "string") { prompt = context; } else { prompt = JSON.stringify(context); } let result; if (llmConfig.system_prompt) { result = await this._execute_chat(prompt, llmConfig); } else { result = await this._execute_completion(prompt, llmConfig); } if (result.isLeft) { return `Error: ${result.left}`; } return this._format_response(result.right, llmConfig); } async _execute_completion(prompt, config) { const params = config.to_provider_params(); return this.provider.complete(prompt, params); } async _execute_chat(prompt, config) { const messages = []; if (config.system_prompt) { messages.push({ role: "system", content: config.system_prompt }); } messages.push({ role: "user", content: prompt }); if (config.assistant_instruction) { messages.push({ role: "assistant", content: config.assistant_instruction }); } const params = config.to_provider_params(); return this.provider.chat(messages, params); } _format_response(response, config) { let content = response; if (response && typeof response === "object" && "content" in response) { content = response.content; } if (config.response_format === "json") { try { if (typeof content === "string") { const start = content.indexOf("{"); const end = content.lastIndexOf("}") + 1; if (start >= 0 && end > start) { return JSON.parse(content.substring(start, end)); } } return content; } catch (e) { return content; } } return content; } }; function promptMonad(prompt, config = {}) { return IO.of(async () => { try { const llmConfig = new LLMConfig(config); const runtime = new LLMRuntime(llmConfig.provider); const params = llmConfig.to_provider_params(); return runtime.provider.complete(prompt, params); } catch (e) { return Either.left(`LLM execution failed: ${e}`); } }); } function chatMonad(messages = null, prompt = null, system_prompt = "", config = {}) { return IO.of(async () => { try { const configData = { ...config }; if (system_prompt) configData.system_prompt = system_prompt; const llmConfig = new LLMConfig(configData); const runtime = new LLMRuntime(llmConfig.provider); const msgs = messages ? [...messages] : []; if (msgs.length === 0) { if (llmConfig.system_prompt) { msgs.push({ role: "system", content: llmConfig.system_prompt }); } if (prompt) { msgs.push({ role: "user", content: prompt }); } if (llmConfig.assistant_instruction) { msgs.push({ role: "assistant", content: llmConfig.assistant_instruction }); } } const params = llmConfig.to_provider_params(); return runtime.provider.chat(msgs, params); } catch (e) { return Either.left(`LLM chat failed: ${e}`); } }); } export { get_provider, LLMRuntime, promptMonad, chatMonad };