@tryhamster/gerbil 1.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +253 -0
- package/bin/cli.js +2 -0
- package/dist/auto-update-BbNHbSU1.mjs +3 -0
- package/dist/browser/index.d.mts +262 -0
- package/dist/browser/index.d.mts.map +1 -0
- package/dist/browser/index.mjs +755 -0
- package/dist/browser/index.mjs.map +1 -0
- package/dist/chrome-backend-C5Un08O4.mjs +771 -0
- package/dist/chrome-backend-C5Un08O4.mjs.map +1 -0
- package/dist/chrome-backend-CtwPENIW.mjs +3 -0
- package/dist/chunk-Ct1HF2bE.mjs +7 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +7078 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/frameworks/express.d.mts +22 -0
- package/dist/frameworks/express.d.mts.map +1 -0
- package/dist/frameworks/express.mjs +123 -0
- package/dist/frameworks/express.mjs.map +1 -0
- package/dist/frameworks/fastify.d.mts +11 -0
- package/dist/frameworks/fastify.d.mts.map +1 -0
- package/dist/frameworks/fastify.mjs +73 -0
- package/dist/frameworks/fastify.mjs.map +1 -0
- package/dist/frameworks/hono.d.mts +14 -0
- package/dist/frameworks/hono.d.mts.map +1 -0
- package/dist/frameworks/hono.mjs +82 -0
- package/dist/frameworks/hono.mjs.map +1 -0
- package/dist/frameworks/next.d.mts +31 -0
- package/dist/frameworks/next.d.mts.map +1 -0
- package/dist/frameworks/next.mjs +116 -0
- package/dist/frameworks/next.mjs.map +1 -0
- package/dist/frameworks/react.d.mts +56 -0
- package/dist/frameworks/react.d.mts.map +1 -0
- package/dist/frameworks/react.mjs +172 -0
- package/dist/frameworks/react.mjs.map +1 -0
- package/dist/frameworks/trpc.d.mts +12 -0
- package/dist/frameworks/trpc.d.mts.map +1 -0
- package/dist/frameworks/trpc.mjs +80 -0
- package/dist/frameworks/trpc.mjs.map +1 -0
- package/dist/gerbil-BfnsFWRE.mjs +644 -0
- package/dist/gerbil-BfnsFWRE.mjs.map +1 -0
- package/dist/gerbil-BjW-z7Fq.mjs +5 -0
- package/dist/gerbil-DZ1k3ChC.d.mts +138 -0
- package/dist/gerbil-DZ1k3ChC.d.mts.map +1 -0
- package/dist/index.d.mts +223 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +13 -0
- package/dist/index.mjs.map +1 -0
- package/dist/integrations/ai-sdk.d.mts +78 -0
- package/dist/integrations/ai-sdk.d.mts.map +1 -0
- package/dist/integrations/ai-sdk.mjs +199 -0
- package/dist/integrations/ai-sdk.mjs.map +1 -0
- package/dist/integrations/langchain.d.mts +41 -0
- package/dist/integrations/langchain.d.mts.map +1 -0
- package/dist/integrations/langchain.mjs +93 -0
- package/dist/integrations/langchain.mjs.map +1 -0
- package/dist/integrations/llamaindex.d.mts +45 -0
- package/dist/integrations/llamaindex.d.mts.map +1 -0
- package/dist/integrations/llamaindex.mjs +86 -0
- package/dist/integrations/llamaindex.mjs.map +1 -0
- package/dist/integrations/mcp-client.d.mts +206 -0
- package/dist/integrations/mcp-client.d.mts.map +1 -0
- package/dist/integrations/mcp-client.mjs +507 -0
- package/dist/integrations/mcp-client.mjs.map +1 -0
- package/dist/integrations/mcp.d.mts +177 -0
- package/dist/integrations/mcp.d.mts.map +1 -0
- package/dist/integrations/mcp.mjs +8 -0
- package/dist/mcp-R8kRLIKb.mjs +348 -0
- package/dist/mcp-R8kRLIKb.mjs.map +1 -0
- package/dist/models-DKULvhOr.mjs +136 -0
- package/dist/models-DKULvhOr.mjs.map +1 -0
- package/dist/models-De2-_GmQ.d.mts +22 -0
- package/dist/models-De2-_GmQ.d.mts.map +1 -0
- package/dist/one-liner-BUQR0nqq.mjs +98 -0
- package/dist/one-liner-BUQR0nqq.mjs.map +1 -0
- package/dist/skills/index.d.mts +390 -0
- package/dist/skills/index.d.mts.map +1 -0
- package/dist/skills/index.mjs +7 -0
- package/dist/skills-D3CEpgDc.mjs +630 -0
- package/dist/skills-D3CEpgDc.mjs.map +1 -0
- package/dist/tools-BsiEE6f2.mjs +567 -0
- package/dist/tools-BsiEE6f2.mjs.map +1 -0
- package/dist/types-BS1N92Jt.d.mts +183 -0
- package/dist/types-BS1N92Jt.d.mts.map +1 -0
- package/dist/utils-7vXqtq2Q.mjs +63 -0
- package/dist/utils-7vXqtq2Q.mjs.map +1 -0
- package/docs/ai-sdk.md +80 -0
- package/docs/architecture/README.md +84 -0
- package/docs/architecture/caching.md +227 -0
- package/docs/architecture/inference.md +176 -0
- package/docs/architecture/overview.md +179 -0
- package/docs/architecture/streaming.md +261 -0
- package/docs/architecture/webgpu.md +213 -0
- package/docs/browser.md +328 -0
- package/docs/cli.md +155 -0
- package/docs/frameworks.md +90 -0
- package/docs/mcp-client.md +224 -0
- package/docs/mcp.md +109 -0
- package/docs/memory.md +229 -0
- package/docs/repl.md +473 -0
- package/docs/skills.md +261 -0
- package/docs/tools.md +304 -0
- package/package.json +207 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools-BsiEE6f2.mjs","names":["GERBIL_DOCS: Record<string, string>","matches: string[]","toolContext: ToolContext | null","results: LoadedToolResult[]"],"sources":["../src/core/docs.ts","../src/core/tools.ts"],"sourcesContent":["/**\n * Gerbil Documentation Content\n *\n * Documentation strings used by the gerbil_docs tool\n * for answering questions about Gerbil features.\n */\n\nexport const GERBIL_DOCS: Record<string, string> = {\n // Core\n quickstart: `Gerbil Quick Start:\n\\`\\`\\`typescript\nimport { Gerbil } from \"gerbil\";\n\nconst g = new Gerbil();\nawait g.loadModel(\"qwen3-0.6b\");\n\n// Generate text\nconst result = await g.generate(\"Write a haiku\");\nconsole.log(result.text);\n\n// Stream responses\nfor await (const chunk of g.stream(\"Tell me a story\")) {\n process.stdout.write(chunk);\n}\n\\`\\`\\``,\n\n generate: `Generate text with options:\n\\`\\`\\`typescript\nconst result = await gerbil.generate(prompt, {\n maxTokens: 256, // Max tokens to generate\n temperature: 0.7, // 0-2, higher = more creative\n topP: 0.9, // Nucleus sampling\n topK: 50, // Top-k sampling\n thinking: false, // Enable reasoning mode (Qwen3)\n system: \"You are...\", // System prompt\n});\n\n// Result contains:\nresult.text // Generated text\nresult.thinking // Reasoning (if thinking=true)\nresult.tokensGenerated\nresult.tokensPerSecond\nresult.totalTime\n\\`\\`\\``,\n\n stream: `Streaming responses:\n\\`\\`\\`typescript\nfor await (const chunk of gerbil.stream(\"Hello\")) {\n process.stdout.write(chunk);\n}\n\n// With options\nconst stream = gerbil.stream(\"Explain React\", {\n maxTokens: 500,\n onToken: (token) => console.log(token),\n});\n\\`\\`\\``,\n\n json: `Structured JSON output with Zod validation:\n\\`\\`\\`typescript\nimport { z } from \"zod\";\n\nconst PersonSchema = z.object({\n name: z.string(),\n age: z.number(),\n city: z.string(),\n});\n\nconst data = await gerbil.json(\"Extract: John is 32 and lives in NYC\", {\n schema: PersonSchema,\n retries: 3, // Retry on validation failure\n temperature: 0.3, // Lower = more consistent\n});\n// { name: \"John\", age: 32, city: \"NYC\" }\n\\`\\`\\``,\n\n thinking: `Thinking mode (chain-of-thought reasoning) - Qwen3 models:\n\\`\\`\\`typescript\nconst result = await gerbil.generate(\"What is 127 × 43?\", {\n thinking: true,\n});\n\nconsole.log(result.thinking);\n// \"Let me calculate step by step: 127 × 43 = 127 × 40 + 127 × 3...\"\n\nconsole.log(result.text);\n// \"5461\"\n\\`\\`\\`\n\nEnable in chat with /no_think or /think commands, or enable_thinking parameter.`,\n\n embed: `Generate embeddings:\n\\`\\`\\`typescript\n// Single text\nconst { vector } = await gerbil.embed(\"Hello world\");\n// vector: number[] (384 dimensions by default)\n\n// Batch\nconst results = await gerbil.embedBatch([\"Hello\", \"World\"]);\n\\`\\`\\``,\n\n // Models\n models: `Available models:\n\n| Model | Size | Best For |\n|-------|------|----------|\n| qwen3-0.6b | ~400MB | General use, reasoning (thinking mode) |\n| qwen2.5-0.5b | ~350MB | Fast and capable |\n| qwen2.5-coder-0.5b | ~400MB | Code generation |\n| smollm2-360m | ~250MB | Very fast, simple tasks |\n| smollm2-135m | ~100MB | Fastest, basic generation |\n| phi-3-mini | ~2.1GB | High quality, larger |\n\nLoad any HuggingFace model:\n\\`\\`\\`typescript\nawait gerbil.loadModel(\"hf:org/model\");\nawait gerbil.loadModel(\"onnx-community/Qwen3-0.6B-ONNX\");\n\\`\\`\\``,\n\n load: `Loading models:\n\\`\\`\\`typescript\nawait gerbil.loadModel(\"qwen3-0.6b\", {\n device: \"auto\", // \"auto\" | \"gpu\" | \"cpu\"\n dtype: \"q4\", // \"q4\" | \"q8\" | \"fp16\" | \"fp32\"\n onProgress: (p) => {\n console.log(p.status, p.progress, p.file);\n },\n});\n\\`\\`\\``,\n\n // AI SDK\n \"ai-sdk\": `Vercel AI SDK v5 integration:\n\\`\\`\\`typescript\nimport { generateText, streamText } from \"ai\";\nimport { gerbil } from \"gerbil/ai\";\n\n// Generate\nconst { text } = await generateText({\n model: gerbil(\"qwen3-0.6b\"),\n prompt: \"Write a commit message\",\n});\n\n// Stream\nconst stream = streamText({\n model: gerbil(\"qwen3-0.6b\"),\n prompt: \"Explain React hooks\",\n});\n\nfor await (const chunk of stream.textStream) {\n process.stdout.write(chunk);\n}\n\n// With thinking mode\nconst { text } = await generateText({\n model: gerbil(\"qwen3-0.6b\", { thinking: true }),\n prompt: \"What is 127 × 43?\",\n});\n\\`\\`\\``,\n\n // Frameworks\n next: `Next.js App Router integration:\n\\`\\`\\`typescript\n// app/api/chat/route.ts\nimport { gerbil } from \"gerbil/next\";\n\nexport const POST = gerbil.handler({ \n model: \"qwen3-0.6b\",\n maxTokens: 500,\n});\n\\`\\`\\``,\n\n express: `Express.js integration:\n\\`\\`\\`typescript\nimport express from \"express\";\nimport { gerbil } from \"gerbil/express\";\n\nconst app = express();\napp.use(\"/ai\", gerbil()());\n\n// Endpoints created:\n// POST /ai/generate - Generate text\n// POST /ai/stream - Stream text\n// POST /ai/json - Structured output\n// POST /ai/embed - Embeddings\n\\`\\`\\``,\n\n react: `React hooks:\n\\`\\`\\`typescript\nimport { useGerbil, useChat } from \"gerbil/react\";\n\nfunction Chat() {\n const { generate, isLoading } = useGerbil();\n \n const handleSubmit = async (prompt: string) => {\n const result = await generate(prompt);\n console.log(result.text);\n };\n}\n\\`\\`\\``,\n\n hono: `Hono integration:\n\\`\\`\\`typescript\nimport { Hono } from \"hono\";\nimport { gerbil } from \"gerbil/hono\";\n\nconst app = new Hono();\napp.route(\"/ai\", await gerbil()());\n\\`\\`\\``,\n\n langchain: `LangChain integration:\n\\`\\`\\`typescript\nimport { GerbilLLM, GerbilEmbeddings } from \"gerbil/langchain\";\n\nconst llm = new GerbilLLM({ model: \"qwen3-0.6b\" });\nconst embeddings = new GerbilEmbeddings();\n\n// Use with chains\nconst chain = new LLMChain({ llm, prompt: template });\n\\`\\`\\``,\n\n mcp: `MCP Server for Claude Desktop & Cursor:\n\\`\\`\\`bash\ngerbil serve --mcp\n\\`\\`\\`\n\nSkills are automatically exposed as MCP tools:\n- gerbil_generate\n- gerbil_commit\n- gerbil_summarize\n- gerbil_explain\n- etc.`,\n\n // Skills\n skills: `Skills are reusable AI tasks with Zod validation:\n\\`\\`\\`typescript\nimport { commit, summarize, explain, review } from \"gerbil/skills\";\n\n// Generate commit message\nconst msg = await commit({ type: \"conventional\", maxLength: 72 });\n\n// Summarize content\nconst summary = await summarize({ \n content: doc, \n length: \"short\",\n format: \"bullets\" \n});\n\n// Explain code\nconst explanation = await explain({ \n content: code, \n level: \"beginner\" \n});\n\n// Code review\nconst feedback = await review({ \n code, \n focus: [\"security\", \"performance\"] \n});\n\\`\\`\\`\n\nBuilt-in skills: commit, summarize, explain, review, test, translate, extract, title`,\n\n \"define-skill\": `Create custom skills:\n\\`\\`\\`typescript\nimport { defineSkill } from \"gerbil/skills\";\nimport { z } from \"zod\";\n\nexport const sentiment = defineSkill({\n name: \"sentiment\",\n description: \"Analyze text sentiment\",\n input: z.object({ \n text: z.string(),\n detailed: z.boolean().default(false)\n }),\n output: z.object({\n sentiment: z.enum([\"positive\", \"negative\", \"neutral\"]),\n confidence: z.number(),\n }),\n temperature: 0.3,\n maxTokens: 100,\n \n async run({ input, gerbil }) {\n return gerbil.json(\\`Sentiment of: \\${input.text}\\`, {\n schema: this.output,\n });\n },\n});\n\\`\\`\\``,\n\n \"load-skills\": `Load skills from files:\n\\`\\`\\`typescript\nimport { loadSkills, useSkill, listSkills } from \"gerbil/skills\";\n\n// Load from directory (*.skill.ts files)\nawait loadSkills(\"./skills\");\n\n// Use by name\nconst skill = useSkill(\"my-skill\");\nconst result = await skill({ text: \"Hello\" });\n\n// List all\nconsole.log(listSkills()); // [\"commit\", \"summarize\", ...]\n\\`\\`\\``,\n\n // CLI\n cli: `CLI commands:\n\\`\\`\\`bash\ngerbil \"Write a haiku\" # Generate text\ngerbil -m qwen3-0.6b \"prompt\" # Specify model\ngerbil --thinking \"127 × 43\" # Enable reasoning\n\ngerbil commit # Git commit message\ngerbil summarize README.md # Summarize file\ngerbil explain src/index.ts # Explain code\n\ngerbil chat # Interactive chat\ngerbil chat --thinking # With reasoning\n\ngerbil serve # HTTP server\ngerbil serve --mcp # MCP server\n\ngerbil models # List models\ngerbil cache # Show cached models\ngerbil bench # Benchmark\ngerbil info # System info\n\ngerbil repl # Interactive TUI\n\\`\\`\\``,\n\n // Tools/Agents\n tools: `Gerbil supports tool calling with Qwen3 models:\n\\`\\`\\`typescript\nimport { defineTool, executeToolCall } from \"gerbil\";\n\nconst weatherTool = defineTool({\n name: \"get_weather\",\n description: \"Get current weather for a city\",\n parameters: z.object({\n city: z.string(),\n }),\n execute: async ({ city }) => {\n return \\`Weather in \\${city}: 72°F, sunny\\`;\n },\n});\n\\`\\`\\`\n\nIn the REPL, use Agent mode (Tab → Agent) to enable tools.`,\n};\n\n/**\n * Get all available documentation topics\n */\nexport function getDocTopics(): string[] {\n return Object.keys(GERBIL_DOCS);\n}\n\n/**\n * Search documentation by topic\n */\nexport function searchDocs(query: string): string | null {\n const words = query.toLowerCase().split(/\\s+/);\n const keyWords = words.filter((w) => w.length > 2);\n\n // Direct key match first\n for (const [key, content] of Object.entries(GERBIL_DOCS)) {\n const keyNorm = key.replace(/-/g, \"\");\n for (const word of keyWords) {\n if (keyNorm === word || key === word || keyNorm.includes(word)) {\n return content;\n }\n }\n }\n\n // Try each word as a potential topic\n for (const word of keyWords) {\n for (const [key, content] of Object.entries(GERBIL_DOCS)) {\n if (key.includes(word) || word.includes(key.replace(/-/g, \"\"))) {\n return content;\n }\n }\n }\n\n // Fallback: search content\n const matches: string[] = [];\n for (const [key, content] of Object.entries(GERBIL_DOCS)) {\n for (const word of keyWords) {\n if (content.toLowerCase().includes(word)) {\n matches.push(`## ${key}\\n${content}`);\n break;\n }\n }\n }\n\n if (matches.length > 0) {\n return matches.slice(0, 2).join(\"\\n\\n---\\n\\n\");\n }\n\n return null;\n}\n","/**\n * Tool Calling System for Gerbil\n *\n * Enables LLMs to call functions/tools during generation.\n * Compatible with Qwen3's tool calling format.\n */\n\nimport { z } from \"zod\";\nimport { getDocTopics, searchDocs } from \"./docs.js\";\nimport { zodToJsonSchema } from \"./utils.js\";\n\n// ============================================\n// Tool Context (for passing gerbil to tools)\n// ============================================\n\nexport type ToolContext = {\n /** Generate text using the loaded model */\n generate: (prompt: string) => Promise<string>;\n};\n\nlet toolContext: ToolContext | null = null;\n\n/**\n * Set the tool context (call this after loading a model)\n */\nexport function setToolContext(ctx: ToolContext): void {\n toolContext = ctx;\n}\n\n/**\n * Get the tool context for use in tools\n */\nexport function getToolContext(): ToolContext | null {\n return toolContext;\n}\n\n// ============================================\n// Tool Definition\n// ============================================\n\nexport type ToolDefinition<TParams = unknown> = {\n /** Tool name (function name) */\n name: string;\n\n /** Description for the LLM */\n description: string;\n\n /** Zod schema for parameters */\n parameters?: z.ZodType<TParams>;\n\n /** The function to execute (receives params and optional context) */\n execute: (params: TParams, ctx?: ToolContext | null) => Promise<string>;\n};\n\nexport type Tool<TParams = unknown> = {\n definition: ToolDefinition<TParams>;\n (params: TParams): Promise<string>;\n};\n\n// ============================================\n// Tool Registry\n// ============================================\n\nconst toolRegistry = new Map<string, Tool<any>>();\n\n/**\n * Define a tool\n */\nexport function defineTool<TParams>(def: ToolDefinition<TParams>): Tool<TParams> {\n const tool = async (params: TParams): Promise<string> => {\n // Validate params if schema provided\n if (def.parameters) {\n const parsed = def.parameters.parse(params);\n return def.execute(parsed, toolContext);\n }\n return def.execute(params, toolContext);\n };\n\n (tool as Tool<TParams>).definition = def;\n\n // Auto-register\n toolRegistry.set(def.name, tool as Tool<any>);\n\n return tool as Tool<TParams>;\n}\n\n/**\n * Get tool by name\n */\nexport function getTool(name: string): Tool<any> | undefined {\n return toolRegistry.get(name);\n}\n\n/**\n * List all tools\n */\nexport function listTools(): string[] {\n return Array.from(toolRegistry.keys());\n}\n\n/**\n * Get all tool definitions for prompt injection\n */\nexport function getToolDefinitions(): ToolDefinition<any>[] {\n return Array.from(toolRegistry.values()).map((t) => t.definition);\n}\n\nexport type LoadedToolResult = {\n name: string;\n loaded: boolean;\n error?: string;\n path: string;\n};\n\n/**\n * Simple tool config (no imports needed in tool files)\n */\nexport type SimpleToolConfig = {\n name: string;\n description: string;\n execute: (params: Record<string, any>) => Promise<string> | string;\n};\n\n/**\n * Load tools from a directory\n * Tools are simple .ts files with a default export config object\n * No module imports - we eval the execute function directly\n */\nexport async function loadTools(dir: string): Promise<LoadedToolResult[]> {\n const fs = await import(\"fs\");\n const pathModule = await import(\"path\");\n\n if (!fs.existsSync(dir)) {\n return [];\n }\n\n const files = fs.readdirSync(dir).filter((f) => f.endsWith(\".tool.ts\") || f.endsWith(\".tool.js\"));\n const results: LoadedToolResult[] = [];\n\n for (const file of files) {\n const filePath = pathModule.join(dir, file);\n const toolName = file.replace(/\\.tool\\.(ts|js)$/, \"\");\n\n try {\n // Read file content directly\n const content = fs.readFileSync(filePath, \"utf-8\");\n\n // Extract the config object using simple parsing\n const config = parseToolFile(content, toolName);\n\n if (config) {\n // Register the tool\n defineTool({\n name: config.name,\n description: config.description,\n execute: async (params, ctx) => {\n // Project tools may or may not accept ctx parameter\n const executeFn = config.execute as any;\n const result = await executeFn(params, ctx);\n return typeof result === \"string\" ? result : String(result);\n },\n });\n results.push({ name: config.name, loaded: true, path: filePath });\n } else {\n results.push({\n name: toolName,\n loaded: false,\n error: \"Could not parse tool config\",\n path: filePath,\n });\n }\n } catch (e) {\n const errorMsg = e instanceof Error ? e.message.split(\"\\n\")[0] : String(e);\n results.push({ name: toolName, loaded: false, error: errorMsg, path: filePath });\n }\n }\n\n return results;\n}\n\n/**\n * Parse a simple tool file and extract the config\n * Expected format:\n * ```\n * export default {\n * name: \"tool_name\",\n * description: \"...\",\n * execute: async (params) => { ... }\n * }\n * ```\n */\nfunction parseToolFile(content: string, fallbackName: string): SimpleToolConfig | null {\n try {\n // Remove TypeScript type annotations for eval\n let cleaned = content\n .replace(/:\\s*\\{[^}]+\\}/g, \"\") // Remove type objects like : { theme?: string }\n .replace(/:\\s*Record<[^>]+>/g, \"\") // Remove Record types\n .replace(/:\\s*string\\b/g, \"\")\n .replace(/:\\s*number\\b/g, \"\")\n .replace(/:\\s*boolean\\b/g, \"\")\n .replace(/:\\s*any\\b/g, \"\")\n .replace(/:\\s*Promise<[^>]+>/g, \"\")\n .replace(/export\\s+default\\s+/, \"return \");\n\n // Remove comments\n cleaned = cleaned.replace(/\\/\\/.*$/gm, \"\").replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n\n // Wrap in function and evaluate\n const fn = new Function(cleaned);\n const config = fn();\n\n if (config && typeof config === \"object\" && config.name && config.execute) {\n return {\n name: config.name || fallbackName,\n description: config.description || \"No description\",\n execute: config.execute,\n };\n }\n return null;\n } catch (e) {\n console.error(\"Failed to parse tool file:\", e);\n return null;\n }\n}\n\n/**\n * Load tools from project .gerbil/tools directory\n */\nexport async function loadProjectTools(): Promise<LoadedToolResult[]> {\n const pathModule = await import(\"path\");\n const toolsDir = pathModule.join(process.cwd(), \".gerbil\", \"tools\");\n return loadTools(toolsDir);\n}\n\n// Re-export for backwards compatibility\nexport { zodToJsonSchema } from \"./utils.js\";\n\n/**\n * Format tools for Qwen3 prompt\n */\nexport function formatToolsForPrompt(tools: ToolDefinition<any>[]): string {\n if (tools.length === 0) {\n return \"\";\n }\n\n const toolDefs = tools\n .map((t) => {\n const params = t.parameters\n ? zodToJsonSchema(t.parameters)\n : { type: \"object\", properties: {} };\n return `## ${t.name}\n\nDescription: ${t.description}\nParameters: ${JSON.stringify(params, null, 2)}`;\n })\n .join(\"\\n\\n\");\n\n return `You are a helpful assistant with access to tools.\n\n# Tools\n\n${toolDefs}\n\n## How to call tools\n\nTo call a tool, use this exact format:\n<tool_call>\n{\"name\": \"tool_name\", \"arguments\": {\"param\": \"value\"}}\n</tool_call>\n\nWhen the user asks about Gerbil (documentation, features, how to use), call gerbil_docs with a topic like: tools, skills, ai-sdk, next, express, generate, stream, models, cli.\n\nFor other questions, respond normally without calling tools.`;\n}\n\n/**\n * Parse tool call from response\n */\nexport function parseToolCall(response: string): { tool: string; params: any } | null {\n try {\n // Try <tool_call> format first (Qwen style)\n const toolCallMatch = response.match(/<tool_call>\\s*([\\s\\S]*?)\\s*<\\/tool_call>/);\n if (toolCallMatch) {\n const parsed = JSON.parse(toolCallMatch[1]);\n if (parsed.name) {\n return { tool: parsed.name, params: parsed.arguments || {} };\n }\n }\n\n // Fallback: look for {\"name\": \"...\", \"arguments\": ...} pattern\n const nameArgsMatch = response.match(\n /\\{\\s*\"name\"\\s*:\\s*\"([^\"]+)\"[^}]*\"arguments\"\\s*:\\s*(\\{[^}]*\\})/,\n );\n if (nameArgsMatch) {\n return { tool: nameArgsMatch[1], params: JSON.parse(nameArgsMatch[2]) };\n }\n\n // Fallback: {\"tool\": \"...\", \"params\": ...} pattern\n const toolParamsMatch = response.match(\n /\\{\\s*\"tool\"\\s*:\\s*\"([^\"]+)\"[^}]*\"params\"\\s*:\\s*(\\{[^}]*\\})/,\n );\n if (toolParamsMatch) {\n return { tool: toolParamsMatch[1], params: JSON.parse(toolParamsMatch[2]) };\n }\n\n // Simple fallback\n const simpleMatch = response.match(/\\{\\s*\"(?:tool|name)\"\\s*:\\s*\"([^\"]+)\"/);\n if (simpleMatch) {\n return { tool: simpleMatch[1], params: {} };\n }\n } catch {}\n return null;\n}\n\n/**\n * Execute a tool call\n */\nexport async function executeToolCall(toolName: string, params: any): Promise<string> {\n const tool = getTool(toolName);\n if (!tool) {\n return `Error: Unknown tool \"${toolName}\"`;\n }\n\n try {\n return await tool(params);\n } catch (e) {\n return `Error executing ${toolName}: ${e}`;\n }\n}\n\n// ============================================\n// Built-in Tools\n// ============================================\n\n/**\n * Docs tool - searches gerbil documentation\n * Documentation content is in core/docs.ts\n */\nexport const docsTool = defineTool({\n name: \"gerbil_docs\",\n description:\n \"Search Gerbil documentation for information about features, API, integrations, skills, and usage examples\",\n parameters: z.object({\n query: z\n .string()\n .optional()\n .default(\"quickstart\")\n .describe(\n \"Topic: quickstart, generate, stream, json, thinking, embed, models, ai-sdk, next, express, react, skills, define-skill, cli, tools\",\n ),\n }),\n execute: async ({ query = \"quickstart\" }) => {\n const result = searchDocs(query);\n if (result) {\n return result;\n }\n const topics = getDocTopics().join(\", \");\n return `No documentation found for \"${query}\". Available topics: ${topics}`;\n },\n});\n"],"mappings":"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"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/core/types.d.ts
|
|
4
|
+
|
|
5
|
+
type ModelConfig = {
|
|
6
|
+
id: string;
|
|
7
|
+
repo: string;
|
|
8
|
+
description: string;
|
|
9
|
+
size: string;
|
|
10
|
+
contextLength: number;
|
|
11
|
+
supportsThinking: boolean;
|
|
12
|
+
supportsJson: boolean;
|
|
13
|
+
family: "qwen" | "smollm" | "phi" | "mistral" | "llama" | "other";
|
|
14
|
+
};
|
|
15
|
+
type ModelSource = {
|
|
16
|
+
type: "builtin" | "huggingface" | "local";
|
|
17
|
+
path: string;
|
|
18
|
+
};
|
|
19
|
+
type GenerateOptions = {
|
|
20
|
+
/** Maximum tokens to generate (default: 256) */
|
|
21
|
+
maxTokens?: number;
|
|
22
|
+
/** Temperature for sampling, 0-2 (default: 0.7) */
|
|
23
|
+
temperature?: number;
|
|
24
|
+
/** Top-p sampling (default: 0.9) */
|
|
25
|
+
topP?: number;
|
|
26
|
+
/** Top-k sampling (default: 50) */
|
|
27
|
+
topK?: number;
|
|
28
|
+
/** Stop sequences */
|
|
29
|
+
stopSequences?: string[];
|
|
30
|
+
/** System prompt */
|
|
31
|
+
system?: string;
|
|
32
|
+
/** Enable thinking/reasoning mode (Qwen3) */
|
|
33
|
+
thinking?: boolean;
|
|
34
|
+
/** Callback for each token (streaming) */
|
|
35
|
+
onToken?: (token: string) => void;
|
|
36
|
+
};
|
|
37
|
+
type GenerateResult = {
|
|
38
|
+
/** Generated text */
|
|
39
|
+
text: string;
|
|
40
|
+
/** Thinking/reasoning (if enabled) */
|
|
41
|
+
thinking?: string;
|
|
42
|
+
/** Tokens generated */
|
|
43
|
+
tokensGenerated: number;
|
|
44
|
+
/** Generation speed */
|
|
45
|
+
tokensPerSecond: number;
|
|
46
|
+
/** Total time in ms */
|
|
47
|
+
totalTime: number;
|
|
48
|
+
/** Why generation stopped */
|
|
49
|
+
finishReason: "stop" | "length" | "error";
|
|
50
|
+
/** Which provider was used (for hybrid mode) */
|
|
51
|
+
provider?: "local" | "openai" | "anthropic";
|
|
52
|
+
/** Whether result came from cache */
|
|
53
|
+
cached?: boolean;
|
|
54
|
+
};
|
|
55
|
+
type JsonOptions<T = unknown> = {
|
|
56
|
+
/** Zod schema for validation */
|
|
57
|
+
schema: z.ZodType<T>;
|
|
58
|
+
/** Number of retries on invalid JSON (default: 3) */
|
|
59
|
+
retries?: number;
|
|
60
|
+
/** Temperature (lower = more deterministic, default: 0.3) */
|
|
61
|
+
temperature?: number;
|
|
62
|
+
/** System prompt override */
|
|
63
|
+
system?: string;
|
|
64
|
+
};
|
|
65
|
+
type EmbedOptions = {
|
|
66
|
+
/** Model to use for embeddings */
|
|
67
|
+
model?: string;
|
|
68
|
+
/** Normalize vectors (default: true) */
|
|
69
|
+
normalize?: boolean;
|
|
70
|
+
};
|
|
71
|
+
type EmbedResult = {
|
|
72
|
+
/** Embedding vector */
|
|
73
|
+
vector: number[];
|
|
74
|
+
/** Original text */
|
|
75
|
+
text: string;
|
|
76
|
+
/** Time in ms */
|
|
77
|
+
totalTime: number;
|
|
78
|
+
};
|
|
79
|
+
type LoadOptions = {
|
|
80
|
+
/** Progress callback */
|
|
81
|
+
onProgress?: (info: ProgressInfo) => void;
|
|
82
|
+
/** Device: 'auto', 'gpu', 'cpu', 'webgpu' (default: 'auto') */
|
|
83
|
+
device?: "auto" | "gpu" | "cpu" | "webgpu";
|
|
84
|
+
/** Quantization: 'q4', 'q8', 'fp16', 'fp32' (default: 'q4') */
|
|
85
|
+
dtype?: "q4" | "q8" | "fp16" | "fp32";
|
|
86
|
+
/** Override context length */
|
|
87
|
+
contextLength?: number;
|
|
88
|
+
};
|
|
89
|
+
type ProgressInfo = {
|
|
90
|
+
status: string;
|
|
91
|
+
progress?: number;
|
|
92
|
+
file?: string;
|
|
93
|
+
loaded?: number;
|
|
94
|
+
total?: number;
|
|
95
|
+
};
|
|
96
|
+
type GerbilConfig = {
|
|
97
|
+
/** Default model */
|
|
98
|
+
model?: string;
|
|
99
|
+
/** Default device */
|
|
100
|
+
device?: "auto" | "gpu" | "cpu";
|
|
101
|
+
/** Default quantization */
|
|
102
|
+
dtype?: "q4" | "q8" | "fp16" | "fp32";
|
|
103
|
+
/** Cache configuration */
|
|
104
|
+
cache?: CacheConfig;
|
|
105
|
+
/** Fallback configuration */
|
|
106
|
+
fallback?: FallbackConfig;
|
|
107
|
+
};
|
|
108
|
+
type CacheConfig = {
|
|
109
|
+
/** Enable caching (default: true) */
|
|
110
|
+
enabled?: boolean;
|
|
111
|
+
/** Time-to-live in seconds (default: 3600) */
|
|
112
|
+
ttl?: number;
|
|
113
|
+
/** Max cache size (default: "500mb") */
|
|
114
|
+
maxSize?: string;
|
|
115
|
+
/** Storage backend */
|
|
116
|
+
storage?: "memory" | "disk" | "redis";
|
|
117
|
+
/** Redis URL (if storage is redis) */
|
|
118
|
+
redisUrl?: string;
|
|
119
|
+
};
|
|
120
|
+
type FallbackConfig = {
|
|
121
|
+
/** Fallback provider */
|
|
122
|
+
provider: "openai" | "anthropic";
|
|
123
|
+
/** API key */
|
|
124
|
+
apiKey: string;
|
|
125
|
+
/** Model to use */
|
|
126
|
+
model: string;
|
|
127
|
+
/** When to fallback */
|
|
128
|
+
when: "timeout" | "error" | "always-verify";
|
|
129
|
+
/** Timeout in ms before fallback (default: 5000) */
|
|
130
|
+
timeout?: number;
|
|
131
|
+
};
|
|
132
|
+
type SessionStats = {
|
|
133
|
+
prompts: number;
|
|
134
|
+
tokensIn: number;
|
|
135
|
+
tokensOut: number;
|
|
136
|
+
avgSpeed: number;
|
|
137
|
+
totalTime: number;
|
|
138
|
+
cacheHits: number;
|
|
139
|
+
cacheMisses: number;
|
|
140
|
+
};
|
|
141
|
+
type ModelStats = {
|
|
142
|
+
modelId: string;
|
|
143
|
+
avgSpeed: number;
|
|
144
|
+
totalGenerations: number;
|
|
145
|
+
totalTokens: number;
|
|
146
|
+
};
|
|
147
|
+
type SystemInfo = {
|
|
148
|
+
version: string;
|
|
149
|
+
model: ModelConfig | null;
|
|
150
|
+
device: {
|
|
151
|
+
backend: string;
|
|
152
|
+
gpu: string | null;
|
|
153
|
+
vram: string | null;
|
|
154
|
+
status: "ready" | "loading" | "error";
|
|
155
|
+
};
|
|
156
|
+
context: {
|
|
157
|
+
max: number;
|
|
158
|
+
used: number;
|
|
159
|
+
available: number;
|
|
160
|
+
};
|
|
161
|
+
cache: {
|
|
162
|
+
location: string;
|
|
163
|
+
size: string;
|
|
164
|
+
modelCount: number;
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
type GerbilModelSettings = {
|
|
168
|
+
/** Enable thinking mode */
|
|
169
|
+
thinking?: boolean;
|
|
170
|
+
/** Device to use */
|
|
171
|
+
device?: "auto" | "gpu" | "cpu";
|
|
172
|
+
/** Quantization level */
|
|
173
|
+
dtype?: "q4" | "q8" | "fp16" | "fp32";
|
|
174
|
+
};
|
|
175
|
+
type GerbilProviderSettings = {
|
|
176
|
+
/** Default device */
|
|
177
|
+
device?: "auto" | "gpu" | "cpu";
|
|
178
|
+
/** Default quantization */
|
|
179
|
+
dtype?: "q4" | "q8" | "fp16" | "fp32";
|
|
180
|
+
};
|
|
181
|
+
//#endregion
|
|
182
|
+
export { SystemInfo as _, GenerateOptions as a, GerbilModelSettings as c, LoadOptions as d, ModelConfig as f, SessionStats as g, ProgressInfo as h, FallbackConfig as i, GerbilProviderSettings as l, ModelStats as m, EmbedOptions as n, GenerateResult as o, ModelSource as p, EmbedResult as r, GerbilConfig as s, CacheConfig as t, JsonOptions as u };
|
|
183
|
+
//# sourceMappingURL=types-BS1N92Jt.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-BS1N92Jt.d.mts","names":[],"sources":["../src/core/types.ts"],"sourcesContent":[],"mappings":";;;;AAqBY,KAXA,WAAA,GAWW;EASX,EAAA,EAAA,MAAA;EA0BA,IAAA,EAAA,MAAA;EA8BA,WAAA,EAAA,MAAW;EAkBX,IAAA,EAAA,MAAA;EAQA,aAAA,EAAW,MAAA;EAeX,gBAAW,EAAA,OAAA;EAcX,YAAA,EAAA,OAAY;EAYZ,MAAA,EAAA,MAAA,GAAY,QAAA,GAWd,KAAA,GAAA,SAGG,GAAA,OAAc,GAAA,OAAA;AAG3B,CAAA;AAiBY,KAtKA,WAAA,GAsKc;EAqBd,IAAA,EAAA,SAAY,GAAA,aAAA,GAAA,OAAA;EAUZ,IAAA,EAAA,MAAA;AAOZ,CAAA;AAyBY,KA5NA,eAAA,GA4NmB;EAWnB;;;;;;;;;;;;;;;;;KA7MA,cAAA;;;;;;;;;;;;;;;;;;KA8BA;;UAEF,CAAA,CAAE,QAAQ;;;;;;;;KAgBR,YAAA;;;;;;KAQA,WAAA;;;;;;;;KAeA,WAAA;;sBAEU;;;;;;;;KAYV,YAAA;;;;;;;KAYA,YAAA;;;;;;;;UAWF;;aAGG;;KAGD,WAAA;;;;;;;;;;;;KAiBA,cAAA;;;;;;;;;;;;KAqBA,YAAA;;;;;;;;;KAUA,UAAA;;;;;;KAOA,UAAA;;SAEH;;;;;;;;;;;;;;;;;;KAuBG,mBAAA;;;;;;;;KAWA,sBAAA"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//#region src/core/utils.ts
|
|
2
|
+
/**
|
|
3
|
+
* Convert Zod schema to JSON Schema (simplified)
|
|
4
|
+
* Handles objects, arrays, primitives, enums, optionals, and defaults
|
|
5
|
+
*/
|
|
6
|
+
function zodToJsonSchema(schema) {
|
|
7
|
+
try {
|
|
8
|
+
if ("_def" in schema) {
|
|
9
|
+
const def = schema._def;
|
|
10
|
+
if (def.typeName === "ZodObject") {
|
|
11
|
+
const shape = def.shape();
|
|
12
|
+
const properties = {};
|
|
13
|
+
const required = [];
|
|
14
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
15
|
+
properties[key] = zodToJsonSchema(value);
|
|
16
|
+
if (!value._def?.typeName?.includes("Optional")) required.push(key);
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties,
|
|
21
|
+
required
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (def.typeName === "ZodString") return {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: def.description
|
|
27
|
+
};
|
|
28
|
+
if (def.typeName === "ZodNumber") return {
|
|
29
|
+
type: "number",
|
|
30
|
+
description: def.description
|
|
31
|
+
};
|
|
32
|
+
if (def.typeName === "ZodBoolean") return { type: "boolean" };
|
|
33
|
+
if (def.typeName === "ZodArray") return {
|
|
34
|
+
type: "array",
|
|
35
|
+
items: zodToJsonSchema(def.type)
|
|
36
|
+
};
|
|
37
|
+
if (def.typeName === "ZodEnum") return {
|
|
38
|
+
type: "string",
|
|
39
|
+
enum: def.values
|
|
40
|
+
};
|
|
41
|
+
if (def.typeName === "ZodOptional") return zodToJsonSchema(def.innerType);
|
|
42
|
+
if (def.typeName === "ZodDefault") return {
|
|
43
|
+
...zodToJsonSchema(def.innerType),
|
|
44
|
+
default: def.defaultValue()
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
} catch {}
|
|
48
|
+
return { type: "string" };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Extract JSON from text (finds first { } or [ ] block)
|
|
52
|
+
*/
|
|
53
|
+
function extractJson(text) {
|
|
54
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
55
|
+
if (jsonMatch) return jsonMatch[0];
|
|
56
|
+
const arrayMatch = text.match(/\[[\s\S]*\]/);
|
|
57
|
+
if (arrayMatch) return arrayMatch[0];
|
|
58
|
+
return text;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
export { zodToJsonSchema as n, extractJson as t };
|
|
63
|
+
//# sourceMappingURL=utils-7vXqtq2Q.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils-7vXqtq2Q.mjs","names":["properties: Record<string, any>","required: string[]"],"sources":["../src/core/utils.ts"],"sourcesContent":["/**\n * Shared utility functions for Gerbil core\n */\n\nimport type { z } from \"zod\";\n\n/**\n * Convert Zod schema to JSON Schema (simplified)\n * Handles objects, arrays, primitives, enums, optionals, and defaults\n */\nexport function zodToJsonSchema(schema: z.ZodType<any>): object {\n try {\n if (\"_def\" in schema) {\n const def = (schema as any)._def;\n\n if (def.typeName === \"ZodObject\") {\n const shape = def.shape();\n const properties: Record<string, any> = {};\n const required: string[] = [];\n\n for (const [key, value] of Object.entries(shape)) {\n properties[key] = zodToJsonSchema(value as z.ZodType<any>);\n // Check if required (not optional)\n if (!(value as any)._def?.typeName?.includes(\"Optional\")) {\n required.push(key);\n }\n }\n\n return { type: \"object\", properties, required };\n }\n if (def.typeName === \"ZodString\") {\n return { type: \"string\", description: def.description };\n }\n if (def.typeName === \"ZodNumber\") {\n return { type: \"number\", description: def.description };\n }\n if (def.typeName === \"ZodBoolean\") {\n return { type: \"boolean\" };\n }\n if (def.typeName === \"ZodArray\") {\n return { type: \"array\", items: zodToJsonSchema(def.type) };\n }\n if (def.typeName === \"ZodEnum\") {\n return { type: \"string\", enum: def.values };\n }\n if (def.typeName === \"ZodOptional\") {\n return zodToJsonSchema(def.innerType);\n }\n if (def.typeName === \"ZodDefault\") {\n const inner = zodToJsonSchema(def.innerType);\n return { ...inner, default: def.defaultValue() };\n }\n }\n } catch {}\n\n return { type: \"string\" };\n}\n\n/**\n * Extract JSON from text (finds first { } or [ ] block)\n */\nexport function extractJson(text: string): string {\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n return jsonMatch[0];\n }\n\n const arrayMatch = text.match(/\\[[\\s\\S]*\\]/);\n if (arrayMatch) {\n return arrayMatch[0];\n }\n\n return text;\n}\n"],"mappings":";;;;;AAUA,SAAgB,gBAAgB,QAAgC;AAC9D,KAAI;AACF,MAAI,UAAU,QAAQ;GACpB,MAAM,MAAO,OAAe;AAE5B,OAAI,IAAI,aAAa,aAAa;IAChC,MAAM,QAAQ,IAAI,OAAO;IACzB,MAAMA,aAAkC,EAAE;IAC1C,MAAMC,WAAqB,EAAE;AAE7B,SAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,EAAE;AAChD,gBAAW,OAAO,gBAAgB,MAAwB;AAE1D,SAAI,CAAE,MAAc,MAAM,UAAU,SAAS,WAAW,CACtD,UAAS,KAAK,IAAI;;AAItB,WAAO;KAAE,MAAM;KAAU;KAAY;KAAU;;AAEjD,OAAI,IAAI,aAAa,YACnB,QAAO;IAAE,MAAM;IAAU,aAAa,IAAI;IAAa;AAEzD,OAAI,IAAI,aAAa,YACnB,QAAO;IAAE,MAAM;IAAU,aAAa,IAAI;IAAa;AAEzD,OAAI,IAAI,aAAa,aACnB,QAAO,EAAE,MAAM,WAAW;AAE5B,OAAI,IAAI,aAAa,WACnB,QAAO;IAAE,MAAM;IAAS,OAAO,gBAAgB,IAAI,KAAK;IAAE;AAE5D,OAAI,IAAI,aAAa,UACnB,QAAO;IAAE,MAAM;IAAU,MAAM,IAAI;IAAQ;AAE7C,OAAI,IAAI,aAAa,cACnB,QAAO,gBAAgB,IAAI,UAAU;AAEvC,OAAI,IAAI,aAAa,aAEnB,QAAO;IAAE,GADK,gBAAgB,IAAI,UAAU;IACzB,SAAS,IAAI,cAAc;IAAE;;SAG9C;AAER,QAAO,EAAE,MAAM,UAAU;;;;;AAM3B,SAAgB,YAAY,MAAsB;CAChD,MAAM,YAAY,KAAK,MAAM,cAAc;AAC3C,KAAI,UACF,QAAO,UAAU;CAGnB,MAAM,aAAa,KAAK,MAAM,cAAc;AAC5C,KAAI,WACF,QAAO,WAAW;AAGpB,QAAO"}
|
package/docs/ai-sdk.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Gerbil + AI SDK
|
|
2
|
+
|
|
3
|
+
Gerbil works as a [Vercel AI SDK v5](https://sdk.vercel.ai/) provider.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { generateText, streamText } from "ai";
|
|
9
|
+
import { gerbil } from "@tryhamster/gerbil/ai";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Generate Text
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
const { text } = await generateText({
|
|
16
|
+
model: gerbil("qwen3-0.6b"),
|
|
17
|
+
prompt: "Write a commit message for adding dark mode",
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Stream Text
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
const stream = streamText({
|
|
25
|
+
model: gerbil("qwen3-0.6b"),
|
|
26
|
+
prompt: "Explain React hooks",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
for await (const chunk of stream.textStream) {
|
|
30
|
+
process.stdout.write(chunk);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## With System Prompt
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
const { text } = await generateText({
|
|
38
|
+
model: gerbil("qwen3-0.6b"),
|
|
39
|
+
system: "You are a helpful coding assistant.",
|
|
40
|
+
prompt: "How do I handle errors in async/await?",
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Model Settings
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { createGerbil } from "@tryhamster/gerbil/ai";
|
|
48
|
+
|
|
49
|
+
// Create provider with defaults
|
|
50
|
+
const local = createGerbil({
|
|
51
|
+
device: "gpu",
|
|
52
|
+
dtype: "q4",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Use with settings
|
|
56
|
+
const { text } = await generateText({
|
|
57
|
+
model: local("qwen3-0.6b", { thinking: true }),
|
|
58
|
+
prompt: "What is 127 × 43?",
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Available Options
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
gerbil(modelId, {
|
|
66
|
+
thinking?: boolean, // Enable reasoning mode (Qwen3)
|
|
67
|
+
device?: "auto" | "gpu" | "cpu",
|
|
68
|
+
dtype?: "q4" | "q8" | "fp16" | "fp32",
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Specification
|
|
73
|
+
|
|
74
|
+
Gerbil implements `LanguageModelV2` from `@ai-sdk/provider`:
|
|
75
|
+
|
|
76
|
+
- `specificationVersion: "v2"`
|
|
77
|
+
- Streaming with `text-start`, `text-delta`, `text-end`, `finish` events
|
|
78
|
+
- Reasoning content type for thinking mode
|
|
79
|
+
|
|
80
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Gerbil Architecture
|
|
2
|
+
|
|
3
|
+
Technical deep-dive into how Gerbil works under the hood.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
| Document | Description |
|
|
8
|
+
|----------|-------------|
|
|
9
|
+
| [Overview](./overview.md) | High-level architecture and design decisions |
|
|
10
|
+
| [Inference Pipeline](./inference.md) | ONNX Runtime, transformers.js, quantization |
|
|
11
|
+
| [WebGPU](./webgpu.md) | GPU acceleration in browser and Node.js |
|
|
12
|
+
| [Streaming](./streaming.md) | Web Worker architecture and token streaming |
|
|
13
|
+
| [Caching](./caching.md) | Model caching strategies |
|
|
14
|
+
|
|
15
|
+
## Quick Overview
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌───────────────────────────────────────────────────────────────────┐
|
|
19
|
+
│ Your Application │
|
|
20
|
+
├───────────────────────────────────────────────────────────────────┤
|
|
21
|
+
│ │
|
|
22
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
|
23
|
+
│ │ Browser │ │ Node.js │ │ CLI/REPL │ │
|
|
24
|
+
│ │ (WebGPU) │ │ (CPU/WebGPU)│ │ (CPU/WebGPU) │ │
|
|
25
|
+
│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
|
|
26
|
+
│ │ │ │ │
|
|
27
|
+
│ ▼ ▼ ▼ │
|
|
28
|
+
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
29
|
+
│ │ Gerbil Core │ │
|
|
30
|
+
│ │ ┌─────────┐ ┌──────────┐ ┌────────┐ ┌───────────────┐ │ │
|
|
31
|
+
│ │ │ Models │ │ Generate │ │ Stream │ │ JSON/Embed │ │ │
|
|
32
|
+
│ │ └────┬────┘ └────┬─────┘ └───┬────┘ └───────┬───────┘ │ │
|
|
33
|
+
│ └───────┼────────────┼────────────┼───────────────┼───────────┘ │
|
|
34
|
+
│ │ │ │ │ │
|
|
35
|
+
│ ▼ ▼ ▼ ▼ │
|
|
36
|
+
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
37
|
+
│ │ transformers.js │ │
|
|
38
|
+
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
|
|
39
|
+
│ │ │ Tokenizer │ │ Model │ │ TextStreamer │ │ │
|
|
40
|
+
│ │ └─────────────┘ └──────┬──────┘ └─────────────────────┘ │ │
|
|
41
|
+
│ └──────────────────────────┼──────────────────────────────────┘ │
|
|
42
|
+
│ │ │
|
|
43
|
+
│ ▼ │
|
|
44
|
+
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
45
|
+
│ │ ONNX Runtime │ │
|
|
46
|
+
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
|
|
47
|
+
│ │ │ WebGPU │ │ CPU │ │ WASM │ │ │
|
|
48
|
+
│ │ │ (Browser) │ │ (Node.js) │ │ (Fallback) │ │ │
|
|
49
|
+
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │
|
|
50
|
+
│ └─────────────────────────────────────────────────────────────┘ │
|
|
51
|
+
│ │
|
|
52
|
+
└───────────────────────────────────────────────────────────────────┘
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Key Design Decisions
|
|
56
|
+
|
|
57
|
+
### 1. transformers.js as the Foundation
|
|
58
|
+
|
|
59
|
+
We use [Hugging Face transformers.js](https://huggingface.co/docs/transformers.js) which provides:
|
|
60
|
+
- Pre-converted ONNX models from Hugging Face Hub
|
|
61
|
+
- Tokenizers that match the original models exactly
|
|
62
|
+
- Unified API across CPU/GPU/WASM backends
|
|
63
|
+
|
|
64
|
+
### 2. WebGPU First
|
|
65
|
+
|
|
66
|
+
WebGPU provides 5-10x speedup over CPU for inference:
|
|
67
|
+
- **Browser**: Native WebGPU via `navigator.gpu`
|
|
68
|
+
- **Node.js**: Headless Chrome as a WebGPU accelerator (ChromeGPUBackend)
|
|
69
|
+
|
|
70
|
+
### 3. Quantization for Speed
|
|
71
|
+
|
|
72
|
+
All models use quantized weights:
|
|
73
|
+
- **q4f16**: 4-bit weights, fp16 compute (WebGPU)
|
|
74
|
+
- **q4**: 4-bit weights, fp32 compute (CPU/WASM)
|
|
75
|
+
|
|
76
|
+
This reduces model size by ~4x and improves inference speed.
|
|
77
|
+
|
|
78
|
+
### 4. Streaming via Web Workers
|
|
79
|
+
|
|
80
|
+
Browser inference runs in a Web Worker to:
|
|
81
|
+
- Keep the UI responsive during model loading
|
|
82
|
+
- Stream tokens in real-time without blocking
|
|
83
|
+
- Isolate GPU memory from the main thread
|
|
84
|
+
|