profclaw 0.0.1-beta.1
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/.env.example +215 -0
- package/CHANGELOG.md +49 -0
- package/CONTRIBUTING.md +119 -0
- package/Dockerfile +107 -0
- package/LICENSE +661 -0
- package/README.md +196 -0
- package/config/agents.yml +55 -0
- package/config/budget.yml +15 -0
- package/config/pricing.yml +46 -0
- package/config/settings.yml +168 -0
- package/dist/adapters/claude-code.d.ts +46 -0
- package/dist/adapters/claude-code.js +215 -0
- package/dist/adapters/index.d.ts +4 -0
- package/dist/adapters/index.js +4 -0
- package/dist/adapters/ollama.d.ts +45 -0
- package/dist/adapters/ollama.js +277 -0
- package/dist/adapters/openclaw.d.ts +60 -0
- package/dist/adapters/openclaw.js +289 -0
- package/dist/adapters/registry.d.ts +56 -0
- package/dist/adapters/registry.js +163 -0
- package/dist/agents/executor.d.ts +78 -0
- package/dist/agents/executor.js +657 -0
- package/dist/agents/index.d.ts +10 -0
- package/dist/agents/index.js +27 -0
- package/dist/agents/stop-conditions.d.ts +108 -0
- package/dist/agents/stop-conditions.js +299 -0
- package/dist/agents/types.d.ts +185 -0
- package/dist/agents/types.js +14 -0
- package/dist/ai/embedding-service.d.ts +96 -0
- package/dist/ai/embedding-service.js +333 -0
- package/dist/auth/api-tokens.d.ts +71 -0
- package/dist/auth/api-tokens.js +235 -0
- package/dist/auth/auth-service.d.ts +75 -0
- package/dist/auth/auth-service.js +557 -0
- package/dist/auth/device-identity.d.ts +96 -0
- package/dist/auth/device-identity.js +250 -0
- package/dist/auth/github-oauth.d.ts +20 -0
- package/dist/auth/github-oauth.js +59 -0
- package/dist/auth/jira-oauth.d.ts +25 -0
- package/dist/auth/jira-oauth.js +75 -0
- package/dist/auth/linear-oauth.d.ts +21 -0
- package/dist/auth/linear-oauth.js +59 -0
- package/dist/auth/middleware.d.ts +44 -0
- package/dist/auth/middleware.js +190 -0
- package/dist/auth/pairing-codes.d.ts +109 -0
- package/dist/auth/pairing-codes.js +274 -0
- package/dist/auth/password.d.ts +53 -0
- package/dist/auth/password.js +126 -0
- package/dist/auth/qr-pairing.d.ts +36 -0
- package/dist/auth/qr-pairing.js +138 -0
- package/dist/auth/slack-oauth.d.ts +31 -0
- package/dist/auth/slack-oauth.js +80 -0
- package/dist/backup/index.d.ts +50 -0
- package/dist/backup/index.js +184 -0
- package/dist/browser/adapters.d.ts +25 -0
- package/dist/browser/adapters.js +32 -0
- package/dist/browser/cli.d.ts +19 -0
- package/dist/browser/cli.js +277 -0
- package/dist/browser/compression.d.ts +32 -0
- package/dist/browser/compression.js +269 -0
- package/dist/browser/extensions.d.ts +144 -0
- package/dist/browser/extensions.js +251 -0
- package/dist/browser/index.d.ts +37 -0
- package/dist/browser/index.js +43 -0
- package/dist/browser/refs.d.ts +46 -0
- package/dist/browser/refs.js +120 -0
- package/dist/browser/search.d.ts +36 -0
- package/dist/browser/search.js +193 -0
- package/dist/browser/service.d.ts +94 -0
- package/dist/browser/service.js +358 -0
- package/dist/browser/snapshot.d.ts +19 -0
- package/dist/browser/snapshot.js +250 -0
- package/dist/browser/tools.d.ts +259 -0
- package/dist/browser/tools.js +463 -0
- package/dist/browser/types.d.ts +134 -0
- package/dist/browser/types.js +17 -0
- package/dist/chat/agentic-executor.d.ts +108 -0
- package/dist/chat/agentic-executor.js +739 -0
- package/dist/chat/api-key-detector.d.ts +41 -0
- package/dist/chat/api-key-detector.js +290 -0
- package/dist/chat/auto-reply.d.ts +90 -0
- package/dist/chat/auto-reply.js +272 -0
- package/dist/chat/conversations.d.ts +91 -0
- package/dist/chat/conversations.js +297 -0
- package/dist/chat/execution/audit.d.ts +192 -0
- package/dist/chat/execution/audit.js +323 -0
- package/dist/chat/execution/executor.d.ts +56 -0
- package/dist/chat/execution/executor.js +654 -0
- package/dist/chat/execution/guardrails.d.ts +98 -0
- package/dist/chat/execution/guardrails.js +571 -0
- package/dist/chat/execution/index.d.ts +61 -0
- package/dist/chat/execution/index.js +129 -0
- package/dist/chat/execution/model-capability.d.ts +34 -0
- package/dist/chat/execution/model-capability.js +96 -0
- package/dist/chat/execution/process-pool.d.ts +110 -0
- package/dist/chat/execution/process-pool.js +290 -0
- package/dist/chat/execution/pty.d.ts +92 -0
- package/dist/chat/execution/pty.js +285 -0
- package/dist/chat/execution/rate-limiter.d.ts +87 -0
- package/dist/chat/execution/rate-limiter.js +305 -0
- package/dist/chat/execution/registry.d.ts +54 -0
- package/dist/chat/execution/registry.js +220 -0
- package/dist/chat/execution/sandbox.d.ts +109 -0
- package/dist/chat/execution/sandbox.js +684 -0
- package/dist/chat/execution/secrets.d.ts +45 -0
- package/dist/chat/execution/secrets.js +338 -0
- package/dist/chat/execution/security.d.ts +179 -0
- package/dist/chat/execution/security.js +739 -0
- package/dist/chat/execution/self-correction.d.ts +162 -0
- package/dist/chat/execution/self-correction.js +557 -0
- package/dist/chat/execution/session-manager.d.ts +79 -0
- package/dist/chat/execution/session-manager.js +356 -0
- package/dist/chat/execution/session-spawn/config.d.ts +37 -0
- package/dist/chat/execution/session-spawn/config.js +83 -0
- package/dist/chat/execution/session-spawn/index.d.ts +10 -0
- package/dist/chat/execution/session-spawn/index.js +10 -0
- package/dist/chat/execution/session-spawn/manager.d.ts +35 -0
- package/dist/chat/execution/session-spawn/manager.js +418 -0
- package/dist/chat/execution/session-spawn/types.d.ts +117 -0
- package/dist/chat/execution/session-spawn/types.js +16 -0
- package/dist/chat/execution/smart-prompts.d.ts +34 -0
- package/dist/chat/execution/smart-prompts.js +151 -0
- package/dist/chat/execution/stream-results.d.ts +65 -0
- package/dist/chat/execution/stream-results.js +156 -0
- package/dist/chat/execution/tool-router.d.ts +76 -0
- package/dist/chat/execution/tool-router.js +296 -0
- package/dist/chat/execution/tools/agents-list.d.ts +49 -0
- package/dist/chat/execution/tools/agents-list.js +165 -0
- package/dist/chat/execution/tools/browser.d.ts +12 -0
- package/dist/chat/execution/tools/browser.js +14 -0
- package/dist/chat/execution/tools/canvas.d.ts +61 -0
- package/dist/chat/execution/tools/canvas.js +150 -0
- package/dist/chat/execution/tools/complete-task.d.ts +72 -0
- package/dist/chat/execution/tools/complete-task.js +104 -0
- package/dist/chat/execution/tools/cron-tool.d.ts +204 -0
- package/dist/chat/execution/tools/cron-tool.js +680 -0
- package/dist/chat/execution/tools/discord-actions.d.ts +120 -0
- package/dist/chat/execution/tools/discord-actions.js +361 -0
- package/dist/chat/execution/tools/exec.d.ts +43 -0
- package/dist/chat/execution/tools/exec.js +223 -0
- package/dist/chat/execution/tools/file-ops.d.ts +175 -0
- package/dist/chat/execution/tools/file-ops.js +723 -0
- package/dist/chat/execution/tools/git.d.ts +143 -0
- package/dist/chat/execution/tools/git.js +395 -0
- package/dist/chat/execution/tools/github.d.ts +42 -0
- package/dist/chat/execution/tools/github.js +184 -0
- package/dist/chat/execution/tools/glinr-ops.d.ts +202 -0
- package/dist/chat/execution/tools/glinr-ops.js +694 -0
- package/dist/chat/execution/tools/image-analyze.d.ts +29 -0
- package/dist/chat/execution/tools/image-analyze.js +127 -0
- package/dist/chat/execution/tools/index.d.ts +64 -0
- package/dist/chat/execution/tools/index.js +245 -0
- package/dist/chat/execution/tools/integrations.d.ts +109 -0
- package/dist/chat/execution/tools/integrations.js +237 -0
- package/dist/chat/execution/tools/link-understand.d.ts +34 -0
- package/dist/chat/execution/tools/link-understand.js +307 -0
- package/dist/chat/execution/tools/maintenance.d.ts +29 -0
- package/dist/chat/execution/tools/maintenance.js +105 -0
- package/dist/chat/execution/tools/memory-tools.d.ts +76 -0
- package/dist/chat/execution/tools/memory-tools.js +278 -0
- package/dist/chat/execution/tools/openai-image-gen.d.ts +51 -0
- package/dist/chat/execution/tools/openai-image-gen.js +212 -0
- package/dist/chat/execution/tools/profclaw-ops.d.ts +204 -0
- package/dist/chat/execution/tools/profclaw-ops.js +665 -0
- package/dist/chat/execution/tools/session-spawn.d.ts +146 -0
- package/dist/chat/execution/tools/session-spawn.js +544 -0
- package/dist/chat/execution/tools/session-status.d.ts +38 -0
- package/dist/chat/execution/tools/session-status.js +185 -0
- package/dist/chat/execution/tools/sessions-list.d.ts +49 -0
- package/dist/chat/execution/tools/sessions-list.js +153 -0
- package/dist/chat/execution/tools/sessions-send.d.ts +48 -0
- package/dist/chat/execution/tools/sessions-send.js +207 -0
- package/dist/chat/execution/tools/sessions-spawn.d.ts +51 -0
- package/dist/chat/execution/tools/sessions-spawn.js +137 -0
- package/dist/chat/execution/tools/slack-actions.d.ts +90 -0
- package/dist/chat/execution/tools/slack-actions.js +272 -0
- package/dist/chat/execution/tools/subagent-orchestrate.d.ts +66 -0
- package/dist/chat/execution/tools/subagent-orchestrate.js +313 -0
- package/dist/chat/execution/tools/system.d.ts +121 -0
- package/dist/chat/execution/tools/system.js +415 -0
- package/dist/chat/execution/tools/telegram-actions.d.ts +126 -0
- package/dist/chat/execution/tools/telegram-actions.js +339 -0
- package/dist/chat/execution/tools/test-run.d.ts +43 -0
- package/dist/chat/execution/tools/test-run.js +391 -0
- package/dist/chat/execution/tools/tts-speak.d.ts +39 -0
- package/dist/chat/execution/tools/tts-speak.js +199 -0
- package/dist/chat/execution/tools/web-fetch.d.ts +42 -0
- package/dist/chat/execution/tools/web-fetch.js +247 -0
- package/dist/chat/execution/tools/web-search.d.ts +53 -0
- package/dist/chat/execution/tools/web-search.js +130 -0
- package/dist/chat/execution/types.d.ts +337 -0
- package/dist/chat/execution/types.js +8 -0
- package/dist/chat/execution/workflows/built-in.d.ts +10 -0
- package/dist/chat/execution/workflows/built-in.js +249 -0
- package/dist/chat/execution/workflows/executor.d.ts +13 -0
- package/dist/chat/execution/workflows/executor.js +259 -0
- package/dist/chat/execution/workflows/index.d.ts +5 -0
- package/dist/chat/execution/workflows/index.js +5 -0
- package/dist/chat/execution/workflows/trigger.d.ts +15 -0
- package/dist/chat/execution/workflows/trigger.js +111 -0
- package/dist/chat/execution/workflows/types.d.ts +80 -0
- package/dist/chat/execution/workflows/types.js +2 -0
- package/dist/chat/failover/error.d.ts +73 -0
- package/dist/chat/failover/error.js +412 -0
- package/dist/chat/failover/index.d.ts +10 -0
- package/dist/chat/failover/index.js +11 -0
- package/dist/chat/failover/model-fallback.d.ts +97 -0
- package/dist/chat/failover/model-fallback.js +328 -0
- package/dist/chat/failover/types.d.ts +47 -0
- package/dist/chat/failover/types.js +8 -0
- package/dist/chat/format/chunk.d.ts +59 -0
- package/dist/chat/format/chunk.js +253 -0
- package/dist/chat/format/index.d.ts +7 -0
- package/dist/chat/format/index.js +7 -0
- package/dist/chat/format/tool-result-formatter.d.ts +30 -0
- package/dist/chat/format/tool-result-formatter.js +177 -0
- package/dist/chat/group.d.ts +232 -0
- package/dist/chat/group.js +311 -0
- package/dist/chat/index.d.ts +21 -0
- package/dist/chat/index.js +30 -0
- package/dist/chat/memory.d.ts +108 -0
- package/dist/chat/memory.js +343 -0
- package/dist/chat/message-handler.d.ts +38 -0
- package/dist/chat/message-handler.js +274 -0
- package/dist/chat/proactive/index.d.ts +167 -0
- package/dist/chat/proactive/index.js +721 -0
- package/dist/chat/prompt-adapter.d.ts +52 -0
- package/dist/chat/prompt-adapter.js +245 -0
- package/dist/chat/providers/dingtalk/index.d.ts +69 -0
- package/dist/chat/providers/dingtalk/index.js +225 -0
- package/dist/chat/providers/discord/index.d.ts +264 -0
- package/dist/chat/providers/discord/index.js +657 -0
- package/dist/chat/providers/feishu/index.d.ts +97 -0
- package/dist/chat/providers/feishu/index.js +362 -0
- package/dist/chat/providers/googlechat/index.d.ts +269 -0
- package/dist/chat/providers/googlechat/index.js +634 -0
- package/dist/chat/providers/imessage/index.d.ts +61 -0
- package/dist/chat/providers/imessage/index.js +166 -0
- package/dist/chat/providers/index.d.ts +76 -0
- package/dist/chat/providers/index.js +430 -0
- package/dist/chat/providers/irc/index.d.ts +101 -0
- package/dist/chat/providers/irc/index.js +557 -0
- package/dist/chat/providers/line/index.d.ts +113 -0
- package/dist/chat/providers/line/index.js +434 -0
- package/dist/chat/providers/matrix/index.d.ts +161 -0
- package/dist/chat/providers/matrix/index.js +559 -0
- package/dist/chat/providers/mattermost/index.d.ts +106 -0
- package/dist/chat/providers/mattermost/index.js +425 -0
- package/dist/chat/providers/msteams/index.d.ts +198 -0
- package/dist/chat/providers/msteams/index.js +694 -0
- package/dist/chat/providers/nextcloud/index.d.ts +52 -0
- package/dist/chat/providers/nextcloud/index.js +156 -0
- package/dist/chat/providers/nostr/index.d.ts +60 -0
- package/dist/chat/providers/nostr/index.js +200 -0
- package/dist/chat/providers/qq/index.d.ts +57 -0
- package/dist/chat/providers/qq/index.js +260 -0
- package/dist/chat/providers/registry.d.ts +89 -0
- package/dist/chat/providers/registry.js +213 -0
- package/dist/chat/providers/signal/index.d.ts +88 -0
- package/dist/chat/providers/signal/index.js +357 -0
- package/dist/chat/providers/slack/index.d.ts +52 -0
- package/dist/chat/providers/slack/index.js +477 -0
- package/dist/chat/providers/synology/index.d.ts +50 -0
- package/dist/chat/providers/synology/index.js +138 -0
- package/dist/chat/providers/telegram/index.d.ts +148 -0
- package/dist/chat/providers/telegram/index.js +492 -0
- package/dist/chat/providers/tlon/index.d.ts +44 -0
- package/dist/chat/providers/tlon/index.js +243 -0
- package/dist/chat/providers/twitch/index.d.ts +79 -0
- package/dist/chat/providers/twitch/index.js +349 -0
- package/dist/chat/providers/types.d.ts +437 -0
- package/dist/chat/providers/types.js +14 -0
- package/dist/chat/providers/webchat/index.d.ts +38 -0
- package/dist/chat/providers/webchat/index.js +214 -0
- package/dist/chat/providers/wecom/index.d.ts +62 -0
- package/dist/chat/providers/wecom/index.js +228 -0
- package/dist/chat/providers/whatsapp/index.d.ts +147 -0
- package/dist/chat/providers/whatsapp/index.js +396 -0
- package/dist/chat/providers/zalo/index.d.ts +54 -0
- package/dist/chat/providers/zalo/index.js +158 -0
- package/dist/chat/providers/zalo-personal/index.d.ts +53 -0
- package/dist/chat/providers/zalo-personal/index.js +212 -0
- package/dist/chat/skills.d.ts +82 -0
- package/dist/chat/skills.js +410 -0
- package/dist/chat/system-prompts.d.ts +76 -0
- package/dist/chat/system-prompts.js +407 -0
- package/dist/chat/tool-handler.d.ts +57 -0
- package/dist/chat/tool-handler.js +336 -0
- package/dist/chat/tools.d.ts +373 -0
- package/dist/chat/tools.js +512 -0
- package/dist/cli/commands/agent.d.ts +3 -0
- package/dist/cli/commands/agent.js +108 -0
- package/dist/cli/commands/auth.d.ts +12 -0
- package/dist/cli/commands/auth.js +301 -0
- package/dist/cli/commands/browser.d.ts +3 -0
- package/dist/cli/commands/browser.js +138 -0
- package/dist/cli/commands/canvas.d.ts +3 -0
- package/dist/cli/commands/canvas.js +155 -0
- package/dist/cli/commands/channels.d.ts +3 -0
- package/dist/cli/commands/channels.js +167 -0
- package/dist/cli/commands/chat.d.ts +9 -0
- package/dist/cli/commands/chat.js +360 -0
- package/dist/cli/commands/completion.d.ts +3 -0
- package/dist/cli/commands/completion.js +170 -0
- package/dist/cli/commands/config.d.ts +3 -0
- package/dist/cli/commands/config.js +193 -0
- package/dist/cli/commands/cost.d.ts +3 -0
- package/dist/cli/commands/cost.js +134 -0
- package/dist/cli/commands/daemon.d.ts +3 -0
- package/dist/cli/commands/daemon.js +381 -0
- package/dist/cli/commands/devices.d.ts +3 -0
- package/dist/cli/commands/devices.js +150 -0
- package/dist/cli/commands/doctor.d.ts +3 -0
- package/dist/cli/commands/doctor.js +295 -0
- package/dist/cli/commands/logs.d.ts +3 -0
- package/dist/cli/commands/logs.js +139 -0
- package/dist/cli/commands/mcp.d.ts +15 -0
- package/dist/cli/commands/mcp.js +157 -0
- package/dist/cli/commands/memory.d.ts +3 -0
- package/dist/cli/commands/memory.js +170 -0
- package/dist/cli/commands/models.d.ts +3 -0
- package/dist/cli/commands/models.js +197 -0
- package/dist/cli/commands/nodes.d.ts +3 -0
- package/dist/cli/commands/nodes.js +193 -0
- package/dist/cli/commands/onboard.d.ts +13 -0
- package/dist/cli/commands/onboard.js +360 -0
- package/dist/cli/commands/plugin.d.ts +15 -0
- package/dist/cli/commands/plugin.js +226 -0
- package/dist/cli/commands/provider.d.ts +16 -0
- package/dist/cli/commands/provider.js +320 -0
- package/dist/cli/commands/security.d.ts +3 -0
- package/dist/cli/commands/security.js +180 -0
- package/dist/cli/commands/serve.d.ts +3 -0
- package/dist/cli/commands/serve.js +179 -0
- package/dist/cli/commands/session.d.ts +3 -0
- package/dist/cli/commands/session.js +172 -0
- package/dist/cli/commands/setup.d.ts +13 -0
- package/dist/cli/commands/setup.js +636 -0
- package/dist/cli/commands/skill.d.ts +15 -0
- package/dist/cli/commands/skill.js +191 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.js +71 -0
- package/dist/cli/commands/summary.d.ts +3 -0
- package/dist/cli/commands/summary.js +160 -0
- package/dist/cli/commands/task.d.ts +3 -0
- package/dist/cli/commands/task.js +170 -0
- package/dist/cli/commands/ticket.d.ts +3 -0
- package/dist/cli/commands/ticket.js +421 -0
- package/dist/cli/commands/tools.d.ts +9 -0
- package/dist/cli/commands/tools.js +508 -0
- package/dist/cli/commands/tui.d.ts +3 -0
- package/dist/cli/commands/tui.js +158 -0
- package/dist/cli/commands/tunnel.d.ts +3 -0
- package/dist/cli/commands/tunnel.js +135 -0
- package/dist/cli/commands/webhooks.d.ts +3 -0
- package/dist/cli/commands/webhooks.js +191 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +127 -0
- package/dist/cli/utils/api.d.ts +17 -0
- package/dist/cli/utils/api.js +63 -0
- package/dist/cli/utils/config.d.ts +31 -0
- package/dist/cli/utils/config.js +81 -0
- package/dist/cli/utils/output.d.ts +58 -0
- package/dist/cli/utils/output.js +148 -0
- package/dist/core/deployment.d.ts +30 -0
- package/dist/core/deployment.js +114 -0
- package/dist/core/sandbox-config.d.ts +63 -0
- package/dist/core/sandbox-config.js +148 -0
- package/dist/costs/budget.d.ts +28 -0
- package/dist/costs/budget.js +57 -0
- package/dist/costs/persistence.d.ts +77 -0
- package/dist/costs/persistence.js +210 -0
- package/dist/costs/pricing.d.ts +16 -0
- package/dist/costs/pricing.js +39 -0
- package/dist/costs/token-tracker.d.ts +34 -0
- package/dist/costs/token-tracker.js +141 -0
- package/dist/cron/heartbeat.d.ts +23 -0
- package/dist/cron/heartbeat.js +109 -0
- package/dist/cron/index.d.ts +22 -0
- package/dist/cron/index.js +37 -0
- package/dist/cron/issue-poller.d.ts +15 -0
- package/dist/cron/issue-poller.js +174 -0
- package/dist/cron/scheduler.d.ts +315 -0
- package/dist/cron/scheduler.js +1008 -0
- package/dist/cron/stale-checker.d.ts +16 -0
- package/dist/cron/stale-checker.js +61 -0
- package/dist/cron/templates.d.ts +107 -0
- package/dist/cron/templates.js +395 -0
- package/dist/diagnostics/exporters.d.ts +21 -0
- package/dist/diagnostics/exporters.js +138 -0
- package/dist/diagnostics/index.d.ts +10 -0
- package/dist/diagnostics/index.js +9 -0
- package/dist/diagnostics/middleware.d.ts +12 -0
- package/dist/diagnostics/middleware.js +58 -0
- package/dist/diagnostics/tracer.d.ts +133 -0
- package/dist/diagnostics/tracer.js +257 -0
- package/dist/discovery/index.d.ts +8 -0
- package/dist/discovery/index.js +7 -0
- package/dist/discovery/mdns.d.ts +40 -0
- package/dist/discovery/mdns.js +249 -0
- package/dist/gateway/index.d.ts +35 -0
- package/dist/gateway/index.js +36 -0
- package/dist/gateway/router.d.ts +110 -0
- package/dist/gateway/router.js +649 -0
- package/dist/gateway/types.d.ts +275 -0
- package/dist/gateway/types.js +2 -0
- package/dist/gateway/workflows.d.ts +37 -0
- package/dist/gateway/workflows.js +748 -0
- package/dist/hooks/agent-webhook.d.ts +277 -0
- package/dist/hooks/agent-webhook.js +197 -0
- package/dist/hooks/index.d.ts +13 -0
- package/dist/hooks/index.js +15 -0
- package/dist/hooks/prompt-submit.d.ts +45 -0
- package/dist/hooks/prompt-submit.js +111 -0
- package/dist/hooks/schemas.d.ts +563 -0
- package/dist/hooks/schemas.js +108 -0
- package/dist/hooks/session-end.d.ts +25 -0
- package/dist/hooks/session-end.js +153 -0
- package/dist/hooks/tool-use.d.ts +41 -0
- package/dist/hooks/tool-use.js +201 -0
- package/dist/hooks/types.d.ts +76 -0
- package/dist/hooks/types.js +7 -0
- package/dist/integrations/cloudflare-tunnel.d.ts +78 -0
- package/dist/integrations/cloudflare-tunnel.js +268 -0
- package/dist/integrations/github-client.d.ts +27 -0
- package/dist/integrations/github-client.js +79 -0
- package/dist/integrations/github-projects.d.ts +97 -0
- package/dist/integrations/github-projects.js +433 -0
- package/dist/integrations/github-ticket-sync.d.ts +82 -0
- package/dist/integrations/github-ticket-sync.js +352 -0
- package/dist/integrations/github.d.ts +11 -0
- package/dist/integrations/github.js +241 -0
- package/dist/integrations/jira-client.d.ts +30 -0
- package/dist/integrations/jira-client.js +67 -0
- package/dist/integrations/jira.d.ts +13 -0
- package/dist/integrations/jira.js +108 -0
- package/dist/integrations/linear-client.d.ts +23 -0
- package/dist/integrations/linear-client.js +52 -0
- package/dist/integrations/linear.d.ts +15 -0
- package/dist/integrations/linear.js +312 -0
- package/dist/integrations/tailscale.d.ts +68 -0
- package/dist/integrations/tailscale.js +173 -0
- package/dist/integrations/web-search.d.ts +112 -0
- package/dist/integrations/web-search.js +287 -0
- package/dist/intelligence/index.d.ts +8 -0
- package/dist/intelligence/index.js +8 -0
- package/dist/intelligence/ollama.d.ts +50 -0
- package/dist/intelligence/ollama.js +213 -0
- package/dist/intelligence/rules.d.ts +39 -0
- package/dist/intelligence/rules.js +308 -0
- package/dist/labels/index.d.ts +93 -0
- package/dist/labels/index.js +248 -0
- package/dist/mcp/browser-tools.d.ts +10 -0
- package/dist/mcp/browser-tools.js +16 -0
- package/dist/mcp/client.d.ts +54 -0
- package/dist/mcp/client.js +153 -0
- package/dist/mcp/index.d.ts +12 -0
- package/dist/mcp/index.js +13 -0
- package/dist/mcp/server.d.ts +40 -0
- package/dist/mcp/server.js +826 -0
- package/dist/mcp/tool-adapter.d.ts +49 -0
- package/dist/mcp/tool-adapter.js +193 -0
- package/dist/memory/experience-store.d.ts +121 -0
- package/dist/memory/experience-store.js +439 -0
- package/dist/memory/index.d.ts +8 -0
- package/dist/memory/index.js +25 -0
- package/dist/memory/memory-service.d.ts +312 -0
- package/dist/memory/memory-service.js +1172 -0
- package/dist/memory/memory-watcher.d.ts +116 -0
- package/dist/memory/memory-watcher.js +239 -0
- package/dist/middleware/logging.d.ts +34 -0
- package/dist/middleware/logging.js +184 -0
- package/dist/middleware/rate-limit.d.ts +29 -0
- package/dist/middleware/rate-limit.js +91 -0
- package/dist/notifications/in-app.d.ts +28 -0
- package/dist/notifications/in-app.js +53 -0
- package/dist/notifications/index.d.ts +3 -0
- package/dist/notifications/index.js +3 -0
- package/dist/notifications/push.d.ts +74 -0
- package/dist/notifications/push.js +175 -0
- package/dist/notifications/slack.d.ts +32 -0
- package/dist/notifications/slack.js +157 -0
- package/dist/plugins/clawhub.d.ts +70 -0
- package/dist/plugins/clawhub.js +260 -0
- package/dist/plugins/loader.d.ts +23 -0
- package/dist/plugins/loader.js +140 -0
- package/dist/plugins/marketplace.d.ts +77 -0
- package/dist/plugins/marketplace.js +275 -0
- package/dist/plugins/registry.d.ts +122 -0
- package/dist/plugins/registry.js +352 -0
- package/dist/plugins/sandbox.d.ts +77 -0
- package/dist/plugins/sandbox.js +287 -0
- package/dist/plugins/scaffolder.d.ts +28 -0
- package/dist/plugins/scaffolder.js +409 -0
- package/dist/plugins/sdk.d.ts +35 -0
- package/dist/plugins/sdk.js +8 -0
- package/dist/plugins/search/brave.d.ts +9 -0
- package/dist/plugins/search/brave.js +157 -0
- package/dist/plugins/search/duckduckgo.d.ts +9 -0
- package/dist/plugins/search/duckduckgo.js +171 -0
- package/dist/plugins/search/index.d.ts +13 -0
- package/dist/plugins/search/index.js +23 -0
- package/dist/plugins/search/searxng.d.ts +9 -0
- package/dist/plugins/search/searxng.js +170 -0
- package/dist/plugins/search/serper.d.ts +9 -0
- package/dist/plugins/search/serper.js +135 -0
- package/dist/plugins/search/tavily.d.ts +9 -0
- package/dist/plugins/search/tavily.js +146 -0
- package/dist/plugins/types.d.ts +174 -0
- package/dist/plugins/types.js +8 -0
- package/dist/projects/index.d.ts +133 -0
- package/dist/projects/index.js +897 -0
- package/dist/projects/types.d.ts +444 -0
- package/dist/projects/types.js +207 -0
- package/dist/providers/adapters/anthropic.d.ts +26 -0
- package/dist/providers/adapters/anthropic.js +306 -0
- package/dist/providers/adapters/base.d.ts +53 -0
- package/dist/providers/adapters/base.js +99 -0
- package/dist/providers/adapters/ollama.d.ts +30 -0
- package/dist/providers/adapters/ollama.js +292 -0
- package/dist/providers/ai-sdk.d.ts +178 -0
- package/dist/providers/ai-sdk.js +1552 -0
- package/dist/providers/core/index.d.ts +8 -0
- package/dist/providers/core/index.js +10 -0
- package/dist/providers/core/models.d.ts +31 -0
- package/dist/providers/core/models.js +949 -0
- package/dist/providers/core/types.d.ts +117 -0
- package/dist/providers/core/types.js +83 -0
- package/dist/providers/index.d.ts +7 -0
- package/dist/providers/index.js +7 -0
- package/dist/providers/registry.d.ts +93 -0
- package/dist/providers/registry.js +228 -0
- package/dist/providers/schema-utils.d.ts +23 -0
- package/dist/providers/schema-utils.js +545 -0
- package/dist/providers/types.d.ts +609 -0
- package/dist/providers/types.js +279 -0
- package/dist/queue/failure-handler.d.ts +93 -0
- package/dist/queue/failure-handler.js +379 -0
- package/dist/queue/index.d.ts +23 -0
- package/dist/queue/index.js +86 -0
- package/dist/queue/memory-queue.d.ts +30 -0
- package/dist/queue/memory-queue.js +429 -0
- package/dist/queue/notifications.d.ts +6 -0
- package/dist/queue/notifications.js +128 -0
- package/dist/queue/task-queue.d.ts +55 -0
- package/dist/queue/task-queue.js +518 -0
- package/dist/queue/webhook-queue.d.ts +47 -0
- package/dist/queue/webhook-queue.js +158 -0
- package/dist/routes/agents.d.ts +4 -0
- package/dist/routes/agents.js +45 -0
- package/dist/routes/auth.d.ts +19 -0
- package/dist/routes/auth.js +492 -0
- package/dist/routes/backup.d.ts +6 -0
- package/dist/routes/backup.js +71 -0
- package/dist/routes/chat.d.ts +10 -0
- package/dist/routes/chat.js +1678 -0
- package/dist/routes/clawhub.d.ts +8 -0
- package/dist/routes/clawhub.js +89 -0
- package/dist/routes/costs.d.ts +4 -0
- package/dist/routes/costs.js +78 -0
- package/dist/routes/cron.d.ts +11 -0
- package/dist/routes/cron.js +708 -0
- package/dist/routes/devices.d.ts +9 -0
- package/dist/routes/devices.js +490 -0
- package/dist/routes/discord.d.ts +15 -0
- package/dist/routes/discord.js +524 -0
- package/dist/routes/dlq.d.ts +4 -0
- package/dist/routes/dlq.js +64 -0
- package/dist/routes/gateway.d.ts +4 -0
- package/dist/routes/gateway.js +253 -0
- package/dist/routes/health.d.ts +29 -0
- package/dist/routes/health.js +296 -0
- package/dist/routes/hooks.d.ts +4 -0
- package/dist/routes/hooks.js +170 -0
- package/dist/routes/import.d.ts +9 -0
- package/dist/routes/import.js +540 -0
- package/dist/routes/index.d.ts +38 -0
- package/dist/routes/index.js +39 -0
- package/dist/routes/integrations.d.ts +10 -0
- package/dist/routes/integrations.js +200 -0
- package/dist/routes/labels.d.ts +8 -0
- package/dist/routes/labels.js +240 -0
- package/dist/routes/mcp.d.ts +14 -0
- package/dist/routes/mcp.js +125 -0
- package/dist/routes/memory.d.ts +10 -0
- package/dist/routes/memory.js +622 -0
- package/dist/routes/notifications.d.ts +9 -0
- package/dist/routes/notifications.js +154 -0
- package/dist/routes/oobe.d.ts +10 -0
- package/dist/routes/oobe.js +405 -0
- package/dist/routes/openapi.d.ts +9 -0
- package/dist/routes/openapi.js +537 -0
- package/dist/routes/plugins.d.ts +8 -0
- package/dist/routes/plugins.js +108 -0
- package/dist/routes/projects.d.ts +8 -0
- package/dist/routes/projects.js +592 -0
- package/dist/routes/push.d.ts +8 -0
- package/dist/routes/push.js +75 -0
- package/dist/routes/search.d.ts +4 -0
- package/dist/routes/search.js +189 -0
- package/dist/routes/security.d.ts +10 -0
- package/dist/routes/security.js +189 -0
- package/dist/routes/settings.d.ts +4 -0
- package/dist/routes/settings.js +119 -0
- package/dist/routes/setup.d.ts +11 -0
- package/dist/routes/setup.js +525 -0
- package/dist/routes/skills.d.ts +13 -0
- package/dist/routes/skills.js +152 -0
- package/dist/routes/slack.d.ts +12 -0
- package/dist/routes/slack.js +999 -0
- package/dist/routes/states.d.ts +8 -0
- package/dist/routes/states.js +123 -0
- package/dist/routes/stats.d.ts +6 -0
- package/dist/routes/stats.js +369 -0
- package/dist/routes/summaries.d.ts +4 -0
- package/dist/routes/summaries.js +344 -0
- package/dist/routes/sync.d.ts +13 -0
- package/dist/routes/sync.js +268 -0
- package/dist/routes/tasks.d.ts +4 -0
- package/dist/routes/tasks.js +473 -0
- package/dist/routes/telegram.d.ts +14 -0
- package/dist/routes/telegram.js +420 -0
- package/dist/routes/tickets.d.ts +4 -0
- package/dist/routes/tickets.js +1090 -0
- package/dist/routes/tokens.d.ts +4 -0
- package/dist/routes/tokens.js +83 -0
- package/dist/routes/tools.d.ts +14 -0
- package/dist/routes/tools.js +790 -0
- package/dist/routes/tunnels.d.ts +8 -0
- package/dist/routes/tunnels.js +145 -0
- package/dist/routes/users.d.ts +15 -0
- package/dist/routes/users.js +998 -0
- package/dist/routes/voice.d.ts +8 -0
- package/dist/routes/voice.js +127 -0
- package/dist/routes/webchat.d.ts +11 -0
- package/dist/routes/webchat.js +173 -0
- package/dist/routes/webhook-dedup.d.ts +11 -0
- package/dist/routes/webhook-dedup.js +49 -0
- package/dist/routes/webhooks.d.ts +4 -0
- package/dist/routes/webhooks.js +71 -0
- package/dist/routes/whatsapp.d.ts +14 -0
- package/dist/routes/whatsapp.js +391 -0
- package/dist/security/audit-scanner.d.ts +32 -0
- package/dist/security/audit-scanner.js +217 -0
- package/dist/security/fs-guard.d.ts +41 -0
- package/dist/security/fs-guard.js +206 -0
- package/dist/security/prompt-guard.d.ts +47 -0
- package/dist/security/prompt-guard.js +198 -0
- package/dist/security/ssrf-guard.d.ts +38 -0
- package/dist/security/ssrf-guard.js +266 -0
- package/dist/security/types.d.ts +92 -0
- package/dist/security/types.js +8 -0
- package/dist/server/route-loader.d.ts +15 -0
- package/dist/server/route-loader.js +135 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +835 -0
- package/dist/settings/index.d.ts +841 -0
- package/dist/settings/index.js +451 -0
- package/dist/skills/compiler.d.ts +83 -0
- package/dist/skills/compiler.js +358 -0
- package/dist/skills/frontmatter.d.ts +33 -0
- package/dist/skills/frontmatter.js +175 -0
- package/dist/skills/index.d.ts +19 -0
- package/dist/skills/index.js +24 -0
- package/dist/skills/installer.d.ts +22 -0
- package/dist/skills/installer.js +220 -0
- package/dist/skills/loader.d.ts +72 -0
- package/dist/skills/loader.js +484 -0
- package/dist/skills/prompt-builder.d.ts +48 -0
- package/dist/skills/prompt-builder.js +135 -0
- package/dist/skills/registry.d.ts +115 -0
- package/dist/skills/registry.js +225 -0
- package/dist/skills/types.d.ts +202 -0
- package/dist/skills/types.js +24 -0
- package/dist/states/index.d.ts +64 -0
- package/dist/states/index.js +142 -0
- package/dist/storage/adapter.d.ts +146 -0
- package/dist/storage/adapter.js +2 -0
- package/dist/storage/index.d.ts +48 -0
- package/dist/storage/index.js +135 -0
- package/dist/storage/libsql.d.ts +99 -0
- package/dist/storage/libsql.js +1873 -0
- package/dist/storage/migrations.d.ts +65 -0
- package/dist/storage/migrations.js +289 -0
- package/dist/storage/schema.d.ts +10185 -0
- package/dist/storage/schema.js +1113 -0
- package/dist/summaries/extractor.d.ts +56 -0
- package/dist/summaries/extractor.js +377 -0
- package/dist/summaries/index.d.ts +13 -0
- package/dist/summaries/index.js +14 -0
- package/dist/summaries/storage.d.ts +51 -0
- package/dist/summaries/storage.js +101 -0
- package/dist/summaries/templates.d.ts +68 -0
- package/dist/summaries/templates.js +245 -0
- package/dist/sync/adapters/base.d.ts +42 -0
- package/dist/sync/adapters/base.js +142 -0
- package/dist/sync/adapters/github.d.ts +45 -0
- package/dist/sync/adapters/github.js +445 -0
- package/dist/sync/adapters/linear.d.ts +40 -0
- package/dist/sync/adapters/linear.js +434 -0
- package/dist/sync/config.d.ts +388 -0
- package/dist/sync/config.js +281 -0
- package/dist/sync/engine.d.ts +112 -0
- package/dist/sync/engine.js +623 -0
- package/dist/sync/index.d.ts +31 -0
- package/dist/sync/index.js +34 -0
- package/dist/sync/integration.d.ts +53 -0
- package/dist/sync/integration.js +231 -0
- package/dist/sync/types.d.ts +249 -0
- package/dist/sync/types.js +30 -0
- package/dist/tickets/index.d.ts +139 -0
- package/dist/tickets/index.js +1406 -0
- package/dist/tickets/types.d.ts +817 -0
- package/dist/tickets/types.js +310 -0
- package/dist/tools/adapters.d.ts +56 -0
- package/dist/tools/adapters.js +183 -0
- package/dist/tools/index.d.ts +66 -0
- package/dist/tools/index.js +105 -0
- package/dist/tools/types.d.ts +117 -0
- package/dist/tools/types.js +20 -0
- package/dist/types/agent.d.ts +101 -0
- package/dist/types/agent.js +2 -0
- package/dist/types/errors.d.ts +150 -0
- package/dist/types/errors.js +338 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +3 -0
- package/dist/types/summary.d.ts +638 -0
- package/dist/types/summary.js +137 -0
- package/dist/types/task.d.ts +123 -0
- package/dist/types/task.js +46 -0
- package/dist/utils/ai-cache.d.ts +72 -0
- package/dist/utils/ai-cache.js +204 -0
- package/dist/utils/ai-inference.d.ts +57 -0
- package/dist/utils/ai-inference.js +190 -0
- package/dist/utils/ai-responder.d.ts +44 -0
- package/dist/utils/ai-responder.js +272 -0
- package/dist/utils/circuit-breaker.d.ts +139 -0
- package/dist/utils/circuit-breaker.js +281 -0
- package/dist/utils/config-loader.d.ts +12 -0
- package/dist/utils/config-loader.js +44 -0
- package/dist/utils/crypto.d.ts +10 -0
- package/dist/utils/crypto.js +59 -0
- package/dist/utils/env-validator.d.ts +44 -0
- package/dist/utils/env-validator.js +236 -0
- package/dist/utils/file-lock.d.ts +85 -0
- package/dist/utils/file-lock.js +204 -0
- package/dist/utils/logger.d.ts +153 -0
- package/dist/utils/logger.js +282 -0
- package/dist/utils/metrics.d.ts +183 -0
- package/dist/utils/metrics.js +345 -0
- package/dist/utils/security.d.ts +90 -0
- package/dist/utils/security.js +347 -0
- package/dist/voice/index.d.ts +79 -0
- package/dist/voice/index.js +144 -0
- package/dist/voice/stt/whisper.d.ts +13 -0
- package/dist/voice/stt/whisper.js +116 -0
- package/dist/voice/tts/elevenlabs.d.ts +18 -0
- package/dist/voice/tts/elevenlabs.js +178 -0
- package/dist/voice/tts/openai-tts.d.ts +19 -0
- package/dist/voice/tts/openai-tts.js +127 -0
- package/dist/voice/tts/system.d.ts +26 -0
- package/dist/voice/tts/system.js +146 -0
- package/dist/voice/wake-word.d.ts +44 -0
- package/dist/voice/wake-word.js +145 -0
- package/docker-compose.yml +222 -0
- package/package.json +154 -0
- package/profclaw.mjs +24 -0
- package/skills/1password/SKILL.md +155 -0
- package/skills/api-tester/SKILL.md +177 -0
- package/skills/apple-notes/SKILL.md +167 -0
- package/skills/apple-reminders/SKILL.md +178 -0
- package/skills/bear-notes/SKILL.md +166 -0
- package/skills/blogwatcher/SKILL.md +177 -0
- package/skills/camsnap/SKILL.md +130 -0
- package/skills/code-generation/SKILL.md +139 -0
- package/skills/code-review/SKILL.md +116 -0
- package/skills/coding-agent/SKILL.md +184 -0
- package/skills/cron-manager/SKILL.md +141 -0
- package/skills/debug-helper/SKILL.md +134 -0
- package/skills/docker-ops/SKILL.md +246 -0
- package/skills/file-manager/SKILL.md +160 -0
- package/skills/git-workflow/SKILL.md +171 -0
- package/skills/github-issues/SKILL.md +244 -0
- package/skills/gog/SKILL.md +191 -0
- package/skills/goplaces/SKILL.md +156 -0
- package/skills/healthcheck/SKILL.md +270 -0
- package/skills/himalaya/SKILL.md +165 -0
- package/skills/mcp-discovery/SKILL.md +109 -0
- package/skills/memory-manager/SKILL.md +151 -0
- package/skills/model-usage/SKILL.md +177 -0
- package/skills/nano-banana-pro/SKILL.md +214 -0
- package/skills/nano-pdf/SKILL.md +165 -0
- package/skills/notion/SKILL.md +147 -0
- package/skills/obsidian/SKILL.md +163 -0
- package/skills/openai-image-gen/SKILL.md +194 -0
- package/skills/openai-whisper/SKILL.md +98 -0
- package/skills/openai-whisper-api/SKILL.md +130 -0
- package/skills/openhue/SKILL.md +203 -0
- package/skills/oracle/SKILL.md +236 -0
- package/skills/phone-control/SKILL.md +201 -0
- package/skills/profclaw-assistant/SKILL.md +81 -0
- package/skills/profclaw-projects/SKILL.md +68 -0
- package/skills/profclaw-tickets/SKILL.md +91 -0
- package/skills/session-logs/SKILL.md +216 -0
- package/skills/sherpa-onnx-tts/SKILL.md +134 -0
- package/skills/skill-creator/SKILL.md +183 -0
- package/skills/songsee/SKILL.md +170 -0
- package/skills/spotify-player/SKILL.md +254 -0
- package/skills/summarize/SKILL.md +155 -0
- package/skills/system-admin/SKILL.md +179 -0
- package/skills/things-mac/SKILL.md +179 -0
- package/skills/tmux/SKILL.md +170 -0
- package/skills/trello/SKILL.md +154 -0
- package/skills/video-frames/SKILL.md +127 -0
- package/skills/weather/SKILL.md +186 -0
- package/skills/web-research/SKILL.md +128 -0
- package/skills/xurl/SKILL.md +198 -0
- package/ui/README.md +73 -0
- package/ui/dist/assets/ActivityView-CkPgKhwm.js +1 -0
- package/ui/dist/assets/AgentList-C3SVdeEz.js +1 -0
- package/ui/dist/assets/AnalyticsDashboard-DVU--vlP.js +1 -0
- package/ui/dist/assets/AreaChart-B5qMEGWj.js +1 -0
- package/ui/dist/assets/CartesianChart-B50bcUJi.js +36 -0
- package/ui/dist/assets/ChatView-BZFJs7w_.js +10 -0
- package/ui/dist/assets/CostsDashboard-ChfH8BlG.js +1 -0
- package/ui/dist/assets/EstimateSelect-CLpB4NQO.js +1 -0
- package/ui/dist/assets/FloatingChatbot-CBzBY9a0.js +2 -0
- package/ui/dist/assets/InviteCodeManagement-DsbdPa6W.js +2 -0
- package/ui/dist/assets/ProjectDetail-BEM3tFXg.js +2 -0
- package/ui/dist/assets/ProjectDetail-D1-0B2xV.css +1 -0
- package/ui/dist/assets/ProjectIcon-CN1FmFAk.js +1 -0
- package/ui/dist/assets/ProjectList-DSDaNhxQ.js +4 -0
- package/ui/dist/assets/Settings-dPVcqC_d.js +6 -0
- package/ui/dist/assets/StatusIndicator-DA3NAo_b.js +1 -0
- package/ui/dist/assets/SummaryDetail-Bjnf9lT8.js +27 -0
- package/ui/dist/assets/SummaryList-ENwKDBKW.js +1 -0
- package/ui/dist/assets/TaskDetail-DnwG6MzB.js +1 -0
- package/ui/dist/assets/TaskList-D9Ug_HiV.js +1 -0
- package/ui/dist/assets/TicketBoard-CviH1571.js +1 -0
- package/ui/dist/assets/TicketDetail-8Jk8O-33.js +1 -0
- package/ui/dist/assets/TicketList-B-iV4rRA.js +1 -0
- package/ui/dist/assets/UserManagement-CPZ8s7s2.js +2 -0
- package/ui/dist/assets/ViewSwitcher-BUyOOWDI.js +1 -0
- package/ui/dist/assets/alert-dialog-BOmnTkN-.js +7 -0
- package/ui/dist/assets/archive-D5my9zOG.js +1 -0
- package/ui/dist/assets/badge-B4ov81_m.js +1 -0
- package/ui/dist/assets/brain-C53ElFLD.js +1 -0
- package/ui/dist/assets/bug-D0TJoCYn.js +1 -0
- package/ui/dist/assets/calendar-BkWIhKa1.js +1 -0
- package/ui/dist/assets/chevron-up-BRTtndUZ.js +1 -0
- package/ui/dist/assets/circle-x-BN89YhbS.js +1 -0
- package/ui/dist/assets/constants-fc7TFI7u.js +1 -0
- package/ui/dist/assets/cpu-B41x1-vV.js +1 -0
- package/ui/dist/assets/database-D9Cnfmzj.js +1 -0
- package/ui/dist/assets/defineProperty-CoXUr7_6.js +2 -0
- package/ui/dist/assets/dialog-DrWB4EYD.js +1 -0
- package/ui/dist/assets/element-adapter-DL0DQilL.js +4 -0
- package/ui/dist/assets/ellipsis-NuUWekvu.js +1 -0
- package/ui/dist/assets/ellipsis-vertical-BueMfuCw.js +1 -0
- package/ui/dist/assets/file-code-HR9829Jc.js +1 -0
- package/ui/dist/assets/folder-open-C_7oYEqe.js +1 -0
- package/ui/dist/assets/funnel-B5gmNXGf.js +1 -0
- package/ui/dist/assets/git-branch-BRMiJmV3.js +1 -0
- package/ui/dist/assets/git-commit-horizontal-CXOUQeJI.js +1 -0
- package/ui/dist/assets/globe-CCspjvus.js +1 -0
- package/ui/dist/assets/hash-g3102kPC.js +1 -0
- package/ui/dist/assets/history-CNqcBBrV.js +1 -0
- package/ui/dist/assets/index-27mTPKuh.js +1 -0
- package/ui/dist/assets/index-BMx3fH-e.js +1 -0
- package/ui/dist/assets/index-C9jNQXzL.js +1 -0
- package/ui/dist/assets/index-CEZ6R2Uw.js +7 -0
- package/ui/dist/assets/index-DURTouJI.js +1 -0
- package/ui/dist/assets/index-WclMPci1.css +1 -0
- package/ui/dist/assets/index-kb4AJXUB.js +67 -0
- package/ui/dist/assets/layers-CLXiHN6o.js +1 -0
- package/ui/dist/assets/layout-dashboard-B07MFhBs.js +1 -0
- package/ui/dist/assets/lightbulb-B2dgG5ks.js +1 -0
- package/ui/dist/assets/link-2-BxNmr9hL.js +1 -0
- package/ui/dist/assets/markdown-renderer-CjU6hBcW.js +36 -0
- package/ui/dist/assets/message-square-Bf2-CVbt.js +1 -0
- package/ui/dist/assets/pencil-D096Ey62.js +1 -0
- package/ui/dist/assets/play-DfdcSI3x.js +1 -0
- package/ui/dist/assets/popover-BV0QJDQq.js +1 -0
- package/ui/dist/assets/progress-DjPEDJfI.js +1 -0
- package/ui/dist/assets/rocket-CPsTc-5G.js +1 -0
- package/ui/dist/assets/select-LsO16slr.js +1 -0
- package/ui/dist/assets/send-BDYBZDUZ.js +1 -0
- package/ui/dist/assets/server-B3k6noce.js +1 -0
- package/ui/dist/assets/settings-2-Cu4xTjhG.js +1 -0
- package/ui/dist/assets/sheet-BScLnPKX.js +1 -0
- package/ui/dist/assets/shield-check-CQqkwufI.js +1 -0
- package/ui/dist/assets/shield-off-ZFruH9KO.js +1 -0
- package/ui/dist/assets/skeleton-CtkIA_MU.js +1 -0
- package/ui/dist/assets/smartphone-DnoksVkc.js +1 -0
- package/ui/dist/assets/square-uD9ZHMfW.js +1 -0
- package/ui/dist/assets/switch-MQPnwGJK.js +1 -0
- package/ui/dist/assets/table-B8cqlywA.js +1 -0
- package/ui/dist/assets/tag-wEJQRWlV.js +1 -0
- package/ui/dist/assets/textarea-D5LJCOGl.js +1 -0
- package/ui/dist/assets/timer-mbGHegmr.js +1 -0
- package/ui/dist/assets/tooltip-Df1CunSp.js +1 -0
- package/ui/dist/assets/trash-2-B4oAXsX-.js +1 -0
- package/ui/dist/assets/trending-up-BGWxvlmZ.js +1 -0
- package/ui/dist/assets/wand-sparkles-CZy4xvL3.js +1 -0
- package/ui/dist/assets/wrench-ByZkjO6m.js +1 -0
- package/ui/dist/audio-worklets/vad-processor.js +132 -0
- package/ui/dist/brand/profclaw-appicon-192.png +0 -0
- package/ui/dist/brand/profclaw-appicon-512.png +0 -0
- package/ui/dist/brand/profclaw-appicon-shadow.svg +51 -0
- package/ui/dist/brand/profclaw-appicon.svg +50 -0
- package/ui/dist/brand/profclaw-favicon-coral-bg-128.png +0 -0
- package/ui/dist/brand/profclaw-favicon-coral-bg-64.png +0 -0
- package/ui/dist/brand/profclaw-favicon-coral-bg.svg +52 -0
- package/ui/dist/brand/profclaw-favicon-coral-body-128.png +0 -0
- package/ui/dist/brand/profclaw-favicon-coral-body.svg +41 -0
- package/ui/dist/brand/profclaw-favicon-shadow-128.png +0 -0
- package/ui/dist/brand/profclaw-favicon-shadow-64.png +0 -0
- package/ui/dist/brand/profclaw-favicon-shadow.svg +51 -0
- package/ui/dist/brand/profclaw-mark-coral-256.png +0 -0
- package/ui/dist/brand/profclaw-mark-coral-64.png +0 -0
- package/ui/dist/brand/profclaw-mark-coral.svg +31 -0
- package/ui/dist/brand/profclaw-mono-white.svg +18 -0
- package/ui/dist/brand/profclaw-text-dark.svg +18 -0
- package/ui/dist/brand/profclaw-text-light.svg +18 -0
- package/ui/dist/brand/profclaw-wordmark-dark.svg +48 -0
- package/ui/dist/brand/profclaw-wordmark-light.svg +48 -0
- package/ui/dist/brand/safari-pinned-tab.svg +15 -0
- package/ui/dist/favicon/android-chrome-192x192.png +0 -0
- package/ui/dist/favicon/android-chrome-384x384.png +0 -0
- package/ui/dist/favicon/android-chrome-512x512.png +0 -0
- package/ui/dist/favicon/apple-touch-icon.png +0 -0
- package/ui/dist/favicon/browserconfig.xml +9 -0
- package/ui/dist/favicon/favicon-16x16.png +0 -0
- package/ui/dist/favicon/favicon-32x32.png +0 -0
- package/ui/dist/favicon/favicon.ico +0 -0
- package/ui/dist/favicon/mstile-150x150.png +0 -0
- package/ui/dist/favicon/safari-pinned-tab.svg +46 -0
- package/ui/dist/favicon/site.webmanifest +22 -0
- package/ui/dist/index.html +32 -0
- package/ui/dist/manifest.json +49 -0
- package/ui/dist/sw.js +124 -0
- package/ui/dist/vite.svg +1 -0
|
@@ -0,0 +1,1552 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* profClaw AI Provider System
|
|
3
|
+
*
|
|
4
|
+
* Built on Vercel AI SDK for production-ready multi-provider support.
|
|
5
|
+
* Supports: Anthropic, OpenAI, Google, Ollama, and more.
|
|
6
|
+
*
|
|
7
|
+
* @see https://sdk.vercel.ai/docs
|
|
8
|
+
*/
|
|
9
|
+
import { generateText, streamText, tool as createTool, jsonSchema } from 'ai';
|
|
10
|
+
// Provider SDKs are lazily imported inside ensureProvider() to reduce startup memory
|
|
11
|
+
// (each SDK is ~15MB; loading all 6 at startup costs ~100MB even if unused)
|
|
12
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
13
|
+
import { randomUUID } from 'node:crypto';
|
|
14
|
+
import { logger } from '../utils/logger.js';
|
|
15
|
+
import { normalizeToolSchema } from './schema-utils.js';
|
|
16
|
+
// AI provider resilience settings (configurable via env)
|
|
17
|
+
const AI_TIMEOUT_MS = parseInt(process.env['AI_PROVIDER_TIMEOUT_MS'] ?? '120000', 10);
|
|
18
|
+
const AI_MAX_RETRIES = parseInt(process.env['AI_MAX_RETRIES'] ?? '2', 10);
|
|
19
|
+
/**
|
|
20
|
+
* Retry wrapper for transient AI provider failures (429, 503, network errors).
|
|
21
|
+
* Uses exponential backoff with a 10s cap.
|
|
22
|
+
*/
|
|
23
|
+
async function withRetry(fn, maxRetries = AI_MAX_RETRIES) {
|
|
24
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
25
|
+
try {
|
|
26
|
+
return await fn();
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const isRetryable = error instanceof Error &&
|
|
30
|
+
(/429|503|rate.limit|too.many|service.unavailable|ECONNRESET|ETIMEDOUT|fetch.failed/i.test(error.message));
|
|
31
|
+
if (!isRetryable || attempt === maxRetries)
|
|
32
|
+
throw error;
|
|
33
|
+
const delay = Math.min(1000 * 2 ** attempt, 10000);
|
|
34
|
+
logger.warn(`[AIProvider] Transient error (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms`, {
|
|
35
|
+
component: 'AIProvider',
|
|
36
|
+
error: error.message,
|
|
37
|
+
});
|
|
38
|
+
await new Promise(r => setTimeout(r, delay));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
throw new Error('Unreachable');
|
|
42
|
+
}
|
|
43
|
+
// Core types & data — single source of truth in core/
|
|
44
|
+
import { ProviderType, PROVIDER_STATUS, } from './core/types.js';
|
|
45
|
+
import { MODEL_ALIASES, MODEL_CATALOG } from './core/models.js';
|
|
46
|
+
// Re-export for consumers that import from providers/ai-sdk or providers/index
|
|
47
|
+
export { ProviderType, PROVIDER_STATUS, MODEL_ALIASES, MODEL_CATALOG, };
|
|
48
|
+
// Pre-compiled regex patterns for performance (avoid recompilation on each call)
|
|
49
|
+
const TOOL_CALL_REGEX = /```tool\s*\n?([\s\S]*?)\n?```/g;
|
|
50
|
+
function asJsonSchemaInput(schema) {
|
|
51
|
+
return schema;
|
|
52
|
+
}
|
|
53
|
+
function toAiMessages(messages) {
|
|
54
|
+
return messages.map((msg) => ({
|
|
55
|
+
role: msg.role,
|
|
56
|
+
content: msg.content,
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
function toToolArguments(args) {
|
|
60
|
+
return typeof args === 'object' && args !== null
|
|
61
|
+
? args
|
|
62
|
+
: {};
|
|
63
|
+
}
|
|
64
|
+
function getToolResultMessage(result) {
|
|
65
|
+
if (typeof result === 'object' && result !== null && typeof result.message === 'string') {
|
|
66
|
+
return result.message;
|
|
67
|
+
}
|
|
68
|
+
return JSON.stringify(result);
|
|
69
|
+
}
|
|
70
|
+
function buildNativeToolSchema(parameters, normalizeForAzure) {
|
|
71
|
+
if (!normalizeForAzure) {
|
|
72
|
+
return parameters;
|
|
73
|
+
}
|
|
74
|
+
const rawJsonSchema = zodToJsonSchema(parameters, {
|
|
75
|
+
$refStrategy: 'none',
|
|
76
|
+
target: 'openAi',
|
|
77
|
+
});
|
|
78
|
+
const normalizedSchema = normalizeToolSchema(rawJsonSchema);
|
|
79
|
+
return jsonSchema(asJsonSchemaInput(normalizedSchema));
|
|
80
|
+
}
|
|
81
|
+
// Provider priority for auto-selection (prefer cloud providers over local)
|
|
82
|
+
const PROVIDER_PRIORITY = [
|
|
83
|
+
'anthropic', // Claude - best for tools/reasoning
|
|
84
|
+
'openai', // GPT-4 - excellent all-around
|
|
85
|
+
'azure', // Azure OpenAI - enterprise
|
|
86
|
+
'google', // Gemini - good multimodal
|
|
87
|
+
'groq', // Fast inference
|
|
88
|
+
'xai', // Grok
|
|
89
|
+
'mistral', // Mistral
|
|
90
|
+
'deepseek', // DeepSeek R1
|
|
91
|
+
'together', // Together AI
|
|
92
|
+
'fireworks', // Fireworks
|
|
93
|
+
'cerebras', // Cerebras
|
|
94
|
+
'openrouter', // OpenRouter (many models)
|
|
95
|
+
'cohere', // Cohere
|
|
96
|
+
'perplexity', // Perplexity (search-focused)
|
|
97
|
+
'copilot', // GitHub Copilot proxy
|
|
98
|
+
'bedrock', // AWS Bedrock
|
|
99
|
+
'zhipu', // Zhipu AI (GLM series)
|
|
100
|
+
'moonshot', // Moonshot AI (Kimi)
|
|
101
|
+
'qwen', // Qwen (Alibaba Cloud)
|
|
102
|
+
'replicate', // Replicate
|
|
103
|
+
'github-models', // GitHub Models
|
|
104
|
+
'volcengine', // Bytedance Doubao
|
|
105
|
+
'byteplus', // BytePlus
|
|
106
|
+
'qianfan', // Baidu Qianfan
|
|
107
|
+
'modelstudio', // ModelStudio
|
|
108
|
+
'minimax', // Minimax
|
|
109
|
+
'xiaomi', // Xiaomi MiLM
|
|
110
|
+
'huggingface', // HuggingFace Inference
|
|
111
|
+
'nvidia-nim', // NVIDIA NIM
|
|
112
|
+
'watsonx', // IBM Watsonx
|
|
113
|
+
'vercel-ai', // Vercel AI Gateway
|
|
114
|
+
'cloudflare-ai', // Cloudflare AI Gateway
|
|
115
|
+
'venice', // Venice AI
|
|
116
|
+
'kilocode', // Kilocode
|
|
117
|
+
'ollama', // Local - lowest priority (often doesn't support tools)
|
|
118
|
+
];
|
|
119
|
+
class AIProviderManager {
|
|
120
|
+
providers = {};
|
|
121
|
+
configs = new Map();
|
|
122
|
+
defaultProvider = 'ollama';
|
|
123
|
+
constructor() {
|
|
124
|
+
// Initialize with environment variables
|
|
125
|
+
this.initFromEnv();
|
|
126
|
+
// Auto-select best provider as default
|
|
127
|
+
this.autoSelectDefaultProvider();
|
|
128
|
+
}
|
|
129
|
+
initFromEnv() {
|
|
130
|
+
// Anthropic
|
|
131
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
132
|
+
this.configure('anthropic', {
|
|
133
|
+
type: 'anthropic',
|
|
134
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
135
|
+
enabled: true,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// OpenAI
|
|
139
|
+
if (process.env.OPENAI_API_KEY) {
|
|
140
|
+
this.configure('openai', {
|
|
141
|
+
type: 'openai',
|
|
142
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
143
|
+
enabled: true,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
// Google (supports multiple env var names)
|
|
147
|
+
const googleKey = process.env.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY;
|
|
148
|
+
if (googleKey) {
|
|
149
|
+
this.configure('google', {
|
|
150
|
+
type: 'google',
|
|
151
|
+
apiKey: googleKey,
|
|
152
|
+
enabled: true,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// Azure OpenAI (supports both standard and Foundry modes)
|
|
156
|
+
if (process.env.AZURE_OPENAI_API_KEY) {
|
|
157
|
+
// Check if using Foundry (custom base URL) or standard (resource name)
|
|
158
|
+
const baseUrl = process.env.AZURE_OPENAI_BASE_URL || process.env.AZURE_OPENAI_ENDPOINT;
|
|
159
|
+
const resourceName = process.env.AZURE_OPENAI_RESOURCE_NAME;
|
|
160
|
+
if (baseUrl || resourceName) {
|
|
161
|
+
this.configure('azure', {
|
|
162
|
+
type: 'azure',
|
|
163
|
+
apiKey: process.env.AZURE_OPENAI_API_KEY,
|
|
164
|
+
baseUrl: baseUrl,
|
|
165
|
+
resourceName: resourceName,
|
|
166
|
+
deploymentName: process.env.AZURE_OPENAI_DEPLOYMENT_NAME,
|
|
167
|
+
apiVersion: process.env.AZURE_OPENAI_API_VERSION,
|
|
168
|
+
enabled: true,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Groq (fast inference)
|
|
173
|
+
if (process.env.GROQ_API_KEY) {
|
|
174
|
+
this.configure('groq', {
|
|
175
|
+
type: 'groq',
|
|
176
|
+
apiKey: process.env.GROQ_API_KEY,
|
|
177
|
+
enabled: true,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// xAI (Grok)
|
|
181
|
+
if (process.env.XAI_API_KEY) {
|
|
182
|
+
this.configure('xai', {
|
|
183
|
+
type: 'xai',
|
|
184
|
+
apiKey: process.env.XAI_API_KEY,
|
|
185
|
+
enabled: true,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
// Mistral
|
|
189
|
+
if (process.env.MISTRAL_API_KEY) {
|
|
190
|
+
this.configure('mistral', {
|
|
191
|
+
type: 'mistral',
|
|
192
|
+
apiKey: process.env.MISTRAL_API_KEY,
|
|
193
|
+
enabled: true,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
// Cohere
|
|
197
|
+
if (process.env.COHERE_API_KEY) {
|
|
198
|
+
this.configure('cohere', {
|
|
199
|
+
type: 'cohere',
|
|
200
|
+
apiKey: process.env.COHERE_API_KEY,
|
|
201
|
+
enabled: true,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
// Perplexity
|
|
205
|
+
if (process.env.PERPLEXITY_API_KEY) {
|
|
206
|
+
this.configure('perplexity', {
|
|
207
|
+
type: 'perplexity',
|
|
208
|
+
apiKey: process.env.PERPLEXITY_API_KEY,
|
|
209
|
+
enabled: true,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// DeepSeek
|
|
213
|
+
if (process.env.DEEPSEEK_API_KEY) {
|
|
214
|
+
this.configure('deepseek', {
|
|
215
|
+
type: 'deepseek',
|
|
216
|
+
apiKey: process.env.DEEPSEEK_API_KEY,
|
|
217
|
+
enabled: true,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
// Together AI
|
|
221
|
+
if (process.env.TOGETHER_API_KEY) {
|
|
222
|
+
this.configure('together', {
|
|
223
|
+
type: 'together',
|
|
224
|
+
apiKey: process.env.TOGETHER_API_KEY,
|
|
225
|
+
enabled: true,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
// Cerebras
|
|
229
|
+
if (process.env.CEREBRAS_API_KEY) {
|
|
230
|
+
this.configure('cerebras', {
|
|
231
|
+
type: 'cerebras',
|
|
232
|
+
apiKey: process.env.CEREBRAS_API_KEY,
|
|
233
|
+
enabled: true,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
// Zhipu AI (BigModel / GLM series)
|
|
237
|
+
const zhipuKey = process.env.BIGMODEL_API_KEY || process.env.ZHIPU_API_KEY;
|
|
238
|
+
if (zhipuKey) {
|
|
239
|
+
this.configure('zhipu', {
|
|
240
|
+
type: 'zhipu',
|
|
241
|
+
apiKey: zhipuKey,
|
|
242
|
+
enabled: true,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
// Fireworks
|
|
246
|
+
if (process.env.FIREWORKS_API_KEY) {
|
|
247
|
+
this.configure('fireworks', {
|
|
248
|
+
type: 'fireworks',
|
|
249
|
+
apiKey: process.env.FIREWORKS_API_KEY,
|
|
250
|
+
enabled: true,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
// OpenRouter (100+ models via one API)
|
|
254
|
+
if (process.env.OPENROUTER_API_KEY) {
|
|
255
|
+
this.configure('openrouter', {
|
|
256
|
+
type: 'openrouter',
|
|
257
|
+
apiKey: process.env.OPENROUTER_API_KEY,
|
|
258
|
+
enabled: true,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
// Ollama (always enabled for local usage)
|
|
262
|
+
this.configure('ollama', {
|
|
263
|
+
type: 'ollama',
|
|
264
|
+
baseUrl: process.env.OLLAMA_BASE_URL || 'http://localhost:11434',
|
|
265
|
+
enabled: true,
|
|
266
|
+
});
|
|
267
|
+
// GitHub Copilot proxy (experimental - requires local copilot-api server)
|
|
268
|
+
// See: https://github.com/ericc-ch/copilot-api
|
|
269
|
+
if (process.env.COPILOT_API_URL) {
|
|
270
|
+
this.configure('copilot', {
|
|
271
|
+
type: 'copilot',
|
|
272
|
+
baseUrl: process.env.COPILOT_API_URL,
|
|
273
|
+
enabled: true,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
// Volcengine (Bytedance Doubao)
|
|
277
|
+
if (process.env.VOLCENGINE_API_KEY) {
|
|
278
|
+
this.configure('volcengine', {
|
|
279
|
+
type: 'volcengine',
|
|
280
|
+
apiKey: process.env.VOLCENGINE_API_KEY,
|
|
281
|
+
enabled: true,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
// BytePlus (international Bytedance)
|
|
285
|
+
if (process.env.BYTEPLUS_API_KEY) {
|
|
286
|
+
this.configure('byteplus', {
|
|
287
|
+
type: 'byteplus',
|
|
288
|
+
apiKey: process.env.BYTEPLUS_API_KEY,
|
|
289
|
+
enabled: true,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
// Baidu Qianfan
|
|
293
|
+
if (process.env.QIANFAN_API_KEY) {
|
|
294
|
+
this.configure('qianfan', {
|
|
295
|
+
type: 'qianfan',
|
|
296
|
+
apiKey: process.env.QIANFAN_API_KEY,
|
|
297
|
+
enabled: true,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
// ModelStudio (Alibaba DashScope compatible mode)
|
|
301
|
+
if (process.env.MODELSTUDIO_API_KEY) {
|
|
302
|
+
this.configure('modelstudio', {
|
|
303
|
+
type: 'modelstudio',
|
|
304
|
+
apiKey: process.env.MODELSTUDIO_API_KEY,
|
|
305
|
+
enabled: true,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
// Minimax
|
|
309
|
+
if (process.env.MINIMAX_API_KEY) {
|
|
310
|
+
this.configure('minimax', {
|
|
311
|
+
type: 'minimax',
|
|
312
|
+
apiKey: process.env.MINIMAX_API_KEY,
|
|
313
|
+
enabled: true,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
// Xiaomi MiLM
|
|
317
|
+
if (process.env.XIAOMI_API_KEY) {
|
|
318
|
+
this.configure('xiaomi', {
|
|
319
|
+
type: 'xiaomi',
|
|
320
|
+
apiKey: process.env.XIAOMI_API_KEY,
|
|
321
|
+
enabled: true,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
// HuggingFace Inference
|
|
325
|
+
if (process.env.HUGGINGFACE_API_TOKEN) {
|
|
326
|
+
this.configure('huggingface', {
|
|
327
|
+
type: 'huggingface',
|
|
328
|
+
apiKey: process.env.HUGGINGFACE_API_TOKEN,
|
|
329
|
+
enabled: true,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
// NVIDIA NIM
|
|
333
|
+
if (process.env.NVIDIA_NIM_API_KEY) {
|
|
334
|
+
this.configure('nvidia-nim', {
|
|
335
|
+
type: 'nvidia-nim',
|
|
336
|
+
apiKey: process.env.NVIDIA_NIM_API_KEY,
|
|
337
|
+
enabled: true,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
// Venice AI
|
|
341
|
+
if (process.env.VENICE_API_KEY) {
|
|
342
|
+
this.configure('venice', {
|
|
343
|
+
type: 'venice',
|
|
344
|
+
apiKey: process.env.VENICE_API_KEY,
|
|
345
|
+
enabled: true,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
// Kilocode
|
|
349
|
+
if (process.env.KILOCODE_API_KEY) {
|
|
350
|
+
this.configure('kilocode', {
|
|
351
|
+
type: 'kilocode',
|
|
352
|
+
apiKey: process.env.KILOCODE_API_KEY,
|
|
353
|
+
enabled: true,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
// Vercel AI Gateway
|
|
357
|
+
if (process.env.VERCEL_AI_API_KEY) {
|
|
358
|
+
this.configure('vercel-ai', {
|
|
359
|
+
type: 'vercel-ai',
|
|
360
|
+
apiKey: process.env.VERCEL_AI_API_KEY,
|
|
361
|
+
enabled: true,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
// Cloudflare AI Gateway
|
|
365
|
+
if (process.env.CLOUDFLARE_AI_API_KEY || process.env.CLOUDFLARE_AI_TOKEN) {
|
|
366
|
+
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
367
|
+
this.configure('cloudflare-ai', {
|
|
368
|
+
type: 'cloudflare-ai',
|
|
369
|
+
apiKey: process.env.CLOUDFLARE_AI_API_KEY || process.env.CLOUDFLARE_AI_TOKEN,
|
|
370
|
+
baseUrl: accountId
|
|
371
|
+
? `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/v1`
|
|
372
|
+
: undefined,
|
|
373
|
+
enabled: true,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
// IBM Watsonx
|
|
377
|
+
if (process.env.WATSONX_API_KEY) {
|
|
378
|
+
this.configure('watsonx', {
|
|
379
|
+
type: 'watsonx',
|
|
380
|
+
apiKey: process.env.WATSONX_API_KEY,
|
|
381
|
+
baseUrl: process.env.WATSONX_URL || 'https://us-south.ml.cloud.ibm.com/ml/v1',
|
|
382
|
+
defaultModel: process.env.WATSONX_PROJECT_ID,
|
|
383
|
+
enabled: true,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
configure(type, config) {
|
|
388
|
+
this.configs.set(type, config);
|
|
389
|
+
// Invalidate cached provider instance so ensureProvider() recreates it on next use
|
|
390
|
+
const key = this.providerKey(type);
|
|
391
|
+
delete this.providers[key];
|
|
392
|
+
logger.info(`[AIProvider] Configured ${type}`);
|
|
393
|
+
}
|
|
394
|
+
/** Map hyphenated provider types to camelCase keys for the providers record */
|
|
395
|
+
providerKey(type) {
|
|
396
|
+
const keyMap = {
|
|
397
|
+
'github-models': 'githubModels',
|
|
398
|
+
'nvidia-nim': 'nvidiaNim',
|
|
399
|
+
'vercel-ai': 'vercelAi',
|
|
400
|
+
'cloudflare-ai': 'cloudflareAi',
|
|
401
|
+
};
|
|
402
|
+
return keyMap[type] ?? type;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Lazily import and instantiate the SDK for a provider on first use.
|
|
406
|
+
* Each SDK is ~15MB; this avoids loading all of them at startup.
|
|
407
|
+
*/
|
|
408
|
+
async ensureProvider(type) {
|
|
409
|
+
const key = this.providerKey(type);
|
|
410
|
+
if (this.providers[key])
|
|
411
|
+
return;
|
|
412
|
+
const config = this.configs.get(type);
|
|
413
|
+
if (!config?.enabled && type !== 'ollama')
|
|
414
|
+
return;
|
|
415
|
+
switch (type) {
|
|
416
|
+
case 'anthropic': {
|
|
417
|
+
if (!config?.apiKey)
|
|
418
|
+
return;
|
|
419
|
+
const { createAnthropic } = await import('@ai-sdk/anthropic');
|
|
420
|
+
this.providers.anthropic = createAnthropic({
|
|
421
|
+
apiKey: config.apiKey,
|
|
422
|
+
baseURL: config.baseUrl,
|
|
423
|
+
});
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
case 'openai': {
|
|
427
|
+
if (!config?.apiKey)
|
|
428
|
+
return;
|
|
429
|
+
const { createOpenAI } = await import('@ai-sdk/openai');
|
|
430
|
+
this.providers.openai = createOpenAI({
|
|
431
|
+
apiKey: config.apiKey,
|
|
432
|
+
baseURL: config.baseUrl,
|
|
433
|
+
});
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
case 'google': {
|
|
437
|
+
if (!config?.apiKey)
|
|
438
|
+
return;
|
|
439
|
+
const { createGoogleGenerativeAI } = await import('@ai-sdk/google');
|
|
440
|
+
this.providers.google = createGoogleGenerativeAI({
|
|
441
|
+
apiKey: config.apiKey,
|
|
442
|
+
baseURL: config.baseUrl,
|
|
443
|
+
});
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
case 'azure': {
|
|
447
|
+
if (!config?.apiKey)
|
|
448
|
+
return;
|
|
449
|
+
const { createAzure } = await import('@ai-sdk/azure');
|
|
450
|
+
const apiVersion = config.apiVersion || '2024-10-21';
|
|
451
|
+
logger.info(`[AIProvider] Azure using API version: ${apiVersion}`);
|
|
452
|
+
if (config.resourceName) {
|
|
453
|
+
this.providers.azure = createAzure({
|
|
454
|
+
apiKey: config.apiKey,
|
|
455
|
+
resourceName: config.resourceName,
|
|
456
|
+
apiVersion,
|
|
457
|
+
});
|
|
458
|
+
logger.info(`[AIProvider] Azure configured with resource: ${config.resourceName}`);
|
|
459
|
+
}
|
|
460
|
+
else if (config.baseUrl) {
|
|
461
|
+
const endpoint = config.baseUrl.replace(/\/$/, '');
|
|
462
|
+
this.providers.azure = createAzure({
|
|
463
|
+
apiKey: config.apiKey,
|
|
464
|
+
baseURL: endpoint,
|
|
465
|
+
apiVersion,
|
|
466
|
+
useDeploymentBasedUrls: true,
|
|
467
|
+
});
|
|
468
|
+
logger.info(`[AIProvider] Azure configured with endpoint: ${endpoint}`);
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
logger.warn('[AIProvider] Azure API key provided but no endpoint or resource name');
|
|
472
|
+
}
|
|
473
|
+
if (config.defaultModel) {
|
|
474
|
+
logger.info(`[AIProvider] Azure default deployment: ${config.defaultModel}`);
|
|
475
|
+
}
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
case 'ollama': {
|
|
479
|
+
const { createOllama } = await import('ai-sdk-ollama');
|
|
480
|
+
this.providers.ollama = createOllama({
|
|
481
|
+
baseURL: config?.baseUrl || 'http://localhost:11434',
|
|
482
|
+
});
|
|
483
|
+
break;
|
|
484
|
+
}
|
|
485
|
+
// All OpenAI-compatible providers share the same SDK
|
|
486
|
+
case 'groq':
|
|
487
|
+
case 'xai':
|
|
488
|
+
case 'mistral':
|
|
489
|
+
case 'cohere':
|
|
490
|
+
case 'perplexity':
|
|
491
|
+
case 'deepseek':
|
|
492
|
+
case 'together':
|
|
493
|
+
case 'cerebras':
|
|
494
|
+
case 'fireworks':
|
|
495
|
+
case 'openrouter':
|
|
496
|
+
case 'zhipu':
|
|
497
|
+
case 'moonshot': {
|
|
498
|
+
if (!config?.apiKey)
|
|
499
|
+
return;
|
|
500
|
+
const { createOpenAI } = await import('@ai-sdk/openai');
|
|
501
|
+
const baseURLs = {
|
|
502
|
+
groq: 'https://api.groq.com/openai/v1',
|
|
503
|
+
xai: 'https://api.x.ai/v1',
|
|
504
|
+
mistral: 'https://api.mistral.ai/v1',
|
|
505
|
+
cohere: 'https://api.cohere.ai/v1',
|
|
506
|
+
perplexity: 'https://api.perplexity.ai',
|
|
507
|
+
deepseek: 'https://api.deepseek.com/v1',
|
|
508
|
+
together: 'https://api.together.xyz/v1',
|
|
509
|
+
cerebras: 'https://api.cerebras.ai/v1',
|
|
510
|
+
fireworks: 'https://api.fireworks.ai/inference/v1',
|
|
511
|
+
openrouter: 'https://openrouter.ai/api/v1',
|
|
512
|
+
zhipu: 'https://open.bigmodel.cn/api/paas/v4',
|
|
513
|
+
moonshot: 'https://api.moonshot.cn/v1',
|
|
514
|
+
};
|
|
515
|
+
this.providers[key] = createOpenAI({
|
|
516
|
+
apiKey: config.apiKey,
|
|
517
|
+
baseURL: baseURLs[type],
|
|
518
|
+
});
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
case 'copilot': {
|
|
522
|
+
if (!config?.baseUrl)
|
|
523
|
+
return;
|
|
524
|
+
const { createOpenAI } = await import('@ai-sdk/openai');
|
|
525
|
+
this.providers.copilot = createOpenAI({
|
|
526
|
+
apiKey: 'copilot',
|
|
527
|
+
baseURL: config.baseUrl,
|
|
528
|
+
});
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
case 'github-models': {
|
|
532
|
+
if (!config?.apiKey)
|
|
533
|
+
return;
|
|
534
|
+
const { createOpenAI } = await import('@ai-sdk/openai');
|
|
535
|
+
this.providers.githubModels = createOpenAI({
|
|
536
|
+
apiKey: config.apiKey,
|
|
537
|
+
baseURL: 'https://models.inference.ai.azure.com',
|
|
538
|
+
});
|
|
539
|
+
break;
|
|
540
|
+
}
|
|
541
|
+
case 'replicate': {
|
|
542
|
+
if (!config?.apiKey)
|
|
543
|
+
return;
|
|
544
|
+
const { createOpenAI } = await import('@ai-sdk/openai');
|
|
545
|
+
this.providers.replicate = createOpenAI({
|
|
546
|
+
apiKey: config.apiKey,
|
|
547
|
+
baseURL: 'https://openai-proxy.replicate.com/v1',
|
|
548
|
+
});
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
// New OpenAI-compatible providers
|
|
552
|
+
case 'volcengine':
|
|
553
|
+
case 'byteplus':
|
|
554
|
+
case 'qianfan':
|
|
555
|
+
case 'modelstudio':
|
|
556
|
+
case 'minimax':
|
|
557
|
+
case 'xiaomi':
|
|
558
|
+
case 'huggingface':
|
|
559
|
+
case 'nvidia-nim':
|
|
560
|
+
case 'venice':
|
|
561
|
+
case 'kilocode':
|
|
562
|
+
case 'vercel-ai':
|
|
563
|
+
case 'cloudflare-ai':
|
|
564
|
+
case 'watsonx': {
|
|
565
|
+
if (!config?.apiKey)
|
|
566
|
+
return;
|
|
567
|
+
const { createOpenAI } = await import('@ai-sdk/openai');
|
|
568
|
+
const newProviderBaseURLs = {
|
|
569
|
+
volcengine: 'https://ark.cn-beijing.volces.com/api/v3',
|
|
570
|
+
byteplus: 'https://ark.byteplush.com/api/v3',
|
|
571
|
+
qianfan: 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxin',
|
|
572
|
+
modelstudio: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
|
573
|
+
minimax: 'https://api.minimax.chat/v1',
|
|
574
|
+
xiaomi: 'https://api.xiaomi.com/ai/v1',
|
|
575
|
+
huggingface: 'https://api-inference.huggingface.co/v1',
|
|
576
|
+
'nvidia-nim': 'https://integrate.api.nvidia.com/v1',
|
|
577
|
+
venice: 'https://api.venice.ai/api/v1',
|
|
578
|
+
kilocode: 'https://api.kilocode.ai/v1',
|
|
579
|
+
'vercel-ai': 'https://gateway.ai.vercel.com/v1',
|
|
580
|
+
watsonx: 'https://us-south.ml.cloud.ibm.com/ml/v1',
|
|
581
|
+
};
|
|
582
|
+
// Cloudflare uses a dynamic URL based on account ID (set during configure)
|
|
583
|
+
const baseURL = type === 'cloudflare-ai'
|
|
584
|
+
? (config.baseUrl || 'https://api.cloudflare.com/client/v4/accounts/unknown/ai/v1')
|
|
585
|
+
: (config.baseUrl || newProviderBaseURLs[type]);
|
|
586
|
+
this.providers[key] = createOpenAI({
|
|
587
|
+
apiKey: config.apiKey,
|
|
588
|
+
baseURL,
|
|
589
|
+
});
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
setDefaultProvider(type) {
|
|
595
|
+
this.defaultProvider = type;
|
|
596
|
+
}
|
|
597
|
+
getDefaultProvider() {
|
|
598
|
+
return this.defaultProvider;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Auto-select the best configured provider as default.
|
|
602
|
+
* Prioritizes cloud providers (that support tools) over Ollama.
|
|
603
|
+
*/
|
|
604
|
+
autoSelectDefaultProvider() {
|
|
605
|
+
for (const provider of PROVIDER_PRIORITY) {
|
|
606
|
+
const config = this.configs.get(provider);
|
|
607
|
+
if (config?.enabled) {
|
|
608
|
+
// For providers that need API keys, verify they have one
|
|
609
|
+
if (provider !== 'ollama' && provider !== 'copilot') {
|
|
610
|
+
if (!config.apiKey && !config.baseUrl)
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
this.defaultProvider = provider;
|
|
614
|
+
logger.info(`[AIProvider] Auto-selected default provider: ${provider}`);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
// Fallback to ollama if nothing else is configured
|
|
619
|
+
this.defaultProvider = 'ollama';
|
|
620
|
+
logger.info('[AIProvider] Using ollama as default (no cloud providers configured)');
|
|
621
|
+
}
|
|
622
|
+
isConfigured(type) {
|
|
623
|
+
const config = this.configs.get(type);
|
|
624
|
+
return config?.enabled ?? false;
|
|
625
|
+
}
|
|
626
|
+
getConfiguredProviders() {
|
|
627
|
+
return Array.from(this.configs.entries())
|
|
628
|
+
.filter(([, config]) => config.enabled)
|
|
629
|
+
.map(([type]) => type);
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Load saved provider configurations from storage
|
|
633
|
+
* Call this after storage is initialized
|
|
634
|
+
*/
|
|
635
|
+
async loadSavedConfigs(loader) {
|
|
636
|
+
try {
|
|
637
|
+
const savedConfigs = await loader();
|
|
638
|
+
let loaded = 0;
|
|
639
|
+
for (const saved of savedConfigs) {
|
|
640
|
+
// Skip if already configured from env with higher priority
|
|
641
|
+
const existing = this.configs.get(saved.type);
|
|
642
|
+
if (existing?.apiKey && saved.type !== 'ollama') {
|
|
643
|
+
logger.info(`[AI] Skipping saved config for ${saved.type} (already configured from env)`);
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
// Configure the provider
|
|
647
|
+
this.configure(saved.type, {
|
|
648
|
+
type: saved.type,
|
|
649
|
+
apiKey: saved.apiKey,
|
|
650
|
+
baseUrl: saved.baseUrl,
|
|
651
|
+
resourceName: saved.resourceName,
|
|
652
|
+
deploymentName: saved.deploymentName,
|
|
653
|
+
apiVersion: saved.apiVersion,
|
|
654
|
+
defaultModel: saved.defaultModel,
|
|
655
|
+
enabled: saved.enabled ?? true,
|
|
656
|
+
});
|
|
657
|
+
logger.info(`[AI] Loaded saved config for ${saved.type}`);
|
|
658
|
+
loaded++;
|
|
659
|
+
}
|
|
660
|
+
// Re-evaluate default provider after loading saved configs
|
|
661
|
+
if (loaded > 0) {
|
|
662
|
+
this.autoSelectDefaultProvider();
|
|
663
|
+
}
|
|
664
|
+
return loaded;
|
|
665
|
+
}
|
|
666
|
+
catch (error) {
|
|
667
|
+
logger.error('[AI] Failed to load saved configs:', error instanceof Error ? error : undefined);
|
|
668
|
+
return 0;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
resolveModel(modelOrAlias) {
|
|
672
|
+
// Check if it's a full provider/model path
|
|
673
|
+
if (modelOrAlias.includes('/')) {
|
|
674
|
+
const [provider, model] = modelOrAlias.split('/');
|
|
675
|
+
return { provider: provider, model };
|
|
676
|
+
}
|
|
677
|
+
// Check aliases
|
|
678
|
+
const alias = MODEL_ALIASES[modelOrAlias.toLowerCase()];
|
|
679
|
+
if (alias) {
|
|
680
|
+
// For Azure, use the configured deployment name instead of the default alias model
|
|
681
|
+
if (alias.provider === 'azure') {
|
|
682
|
+
const azureConfig = this.configs.get('azure');
|
|
683
|
+
if (azureConfig?.defaultModel) {
|
|
684
|
+
return { provider: 'azure', model: azureConfig.defaultModel };
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
return alias;
|
|
688
|
+
}
|
|
689
|
+
// Try to find in catalog
|
|
690
|
+
const catalogModel = MODEL_CATALOG.find((m) => m.id === modelOrAlias);
|
|
691
|
+
if (catalogModel) {
|
|
692
|
+
return { provider: catalogModel.provider, model: catalogModel.id };
|
|
693
|
+
}
|
|
694
|
+
// Default to the auto-selected default provider
|
|
695
|
+
// Use the provider's default model if configured, otherwise use the given model name
|
|
696
|
+
const defaultConfig = this.configs.get(this.defaultProvider);
|
|
697
|
+
const defaultModel = defaultConfig?.defaultModel || modelOrAlias;
|
|
698
|
+
return { provider: this.defaultProvider, model: defaultModel };
|
|
699
|
+
}
|
|
700
|
+
async getModel(provider, modelId) {
|
|
701
|
+
await this.ensureProvider(provider);
|
|
702
|
+
const key = this.providerKey(provider);
|
|
703
|
+
const instance = this.providers[key];
|
|
704
|
+
if (!instance) {
|
|
705
|
+
const envHints = {
|
|
706
|
+
anthropic: 'Set ANTHROPIC_API_KEY.',
|
|
707
|
+
openai: 'Set OPENAI_API_KEY.',
|
|
708
|
+
google: 'Set GOOGLE_API_KEY.',
|
|
709
|
+
azure: 'Set AZURE_OPENAI_API_KEY and AZURE_OPENAI_RESOURCE_NAME.',
|
|
710
|
+
groq: 'Set GROQ_API_KEY.',
|
|
711
|
+
ollama: 'Check Ollama is running.',
|
|
712
|
+
xai: 'Set XAI_API_KEY.',
|
|
713
|
+
mistral: 'Set MISTRAL_API_KEY.',
|
|
714
|
+
cohere: 'Set COHERE_API_KEY.',
|
|
715
|
+
perplexity: 'Set PERPLEXITY_API_KEY.',
|
|
716
|
+
deepseek: 'Set DEEPSEEK_API_KEY.',
|
|
717
|
+
together: 'Set TOGETHER_API_KEY.',
|
|
718
|
+
cerebras: 'Set CEREBRAS_API_KEY.',
|
|
719
|
+
fireworks: 'Set FIREWORKS_API_KEY.',
|
|
720
|
+
openrouter: 'Set OPENROUTER_API_KEY.',
|
|
721
|
+
copilot: 'Set COPILOT_API_URL.',
|
|
722
|
+
volcengine: 'Set VOLCENGINE_API_KEY.',
|
|
723
|
+
byteplus: 'Set BYTEPLUS_API_KEY.',
|
|
724
|
+
qianfan: 'Set QIANFAN_API_KEY.',
|
|
725
|
+
modelstudio: 'Set MODELSTUDIO_API_KEY.',
|
|
726
|
+
minimax: 'Set MINIMAX_API_KEY.',
|
|
727
|
+
xiaomi: 'Set XIAOMI_API_KEY.',
|
|
728
|
+
huggingface: 'Set HUGGINGFACE_API_TOKEN.',
|
|
729
|
+
'nvidia-nim': 'Set NVIDIA_NIM_API_KEY.',
|
|
730
|
+
venice: 'Set VENICE_API_KEY.',
|
|
731
|
+
kilocode: 'Set KILOCODE_API_KEY.',
|
|
732
|
+
'vercel-ai': 'Set VERCEL_AI_API_KEY.',
|
|
733
|
+
'cloudflare-ai': 'Set CLOUDFLARE_AI_API_KEY and CLOUDFLARE_ACCOUNT_ID.',
|
|
734
|
+
watsonx: 'Set WATSONX_API_KEY.',
|
|
735
|
+
};
|
|
736
|
+
throw new Error(`${provider} not configured. ${envHints[provider] || ''}`);
|
|
737
|
+
}
|
|
738
|
+
// Azure uses .chat() method for deployment-based access
|
|
739
|
+
if (provider === 'azure') {
|
|
740
|
+
let deployment = modelId;
|
|
741
|
+
if (modelId === 'default') {
|
|
742
|
+
const azureConfig = this.configs.get('azure');
|
|
743
|
+
deployment = azureConfig?.defaultModel || modelId;
|
|
744
|
+
if (deployment === 'default') {
|
|
745
|
+
throw new Error('Azure deployment not configured. Set defaultModel in Azure config or AZURE_OPENAI_DEPLOYMENT_NAME.');
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return instance.chat(deployment);
|
|
749
|
+
}
|
|
750
|
+
// OpenAI-compatible providers must use .chat() - the default instance() call
|
|
751
|
+
// uses the Responses API which third-party providers don't support
|
|
752
|
+
const OPENAI_COMPAT_PROVIDERS = new Set([
|
|
753
|
+
'openai', 'groq', 'xai', 'mistral', 'cohere', 'perplexity',
|
|
754
|
+
'deepseek', 'together', 'cerebras', 'fireworks', 'openrouter', 'copilot',
|
|
755
|
+
'zhipu', 'moonshot',
|
|
756
|
+
]);
|
|
757
|
+
if (OPENAI_COMPAT_PROVIDERS.has(provider) && typeof instance.chat === 'function') {
|
|
758
|
+
return instance.chat(modelId);
|
|
759
|
+
}
|
|
760
|
+
return instance(modelId);
|
|
761
|
+
}
|
|
762
|
+
getModelInfo(modelId) {
|
|
763
|
+
return MODEL_CATALOG.find((m) => m.id === modelId);
|
|
764
|
+
}
|
|
765
|
+
getModelsForProvider(provider) {
|
|
766
|
+
return MODEL_CATALOG.filter((m) => m.provider === provider);
|
|
767
|
+
}
|
|
768
|
+
getAllModels() {
|
|
769
|
+
return [...MODEL_CATALOG];
|
|
770
|
+
}
|
|
771
|
+
async chat(request) {
|
|
772
|
+
const startTime = Date.now();
|
|
773
|
+
// Resolve model
|
|
774
|
+
const modelRef = request.model
|
|
775
|
+
? this.resolveModel(request.model)
|
|
776
|
+
: { provider: this.defaultProvider, model: 'llama3.2' };
|
|
777
|
+
const model = await this.getModel(modelRef.provider, modelRef.model);
|
|
778
|
+
// Convert messages to AI SDK format
|
|
779
|
+
const messages = request.messages.map((msg) => ({
|
|
780
|
+
role: msg.role,
|
|
781
|
+
content: msg.content,
|
|
782
|
+
}));
|
|
783
|
+
// Add system prompt if provided
|
|
784
|
+
if (request.systemPrompt) {
|
|
785
|
+
messages.unshift({ role: 'system', content: request.systemPrompt });
|
|
786
|
+
}
|
|
787
|
+
try {
|
|
788
|
+
const result = await withRetry(() => generateText({
|
|
789
|
+
model,
|
|
790
|
+
messages,
|
|
791
|
+
temperature: request.temperature ?? 0.7,
|
|
792
|
+
maxOutputTokens: request.maxTokens ?? 4096,
|
|
793
|
+
abortSignal: AbortSignal.timeout(AI_TIMEOUT_MS),
|
|
794
|
+
}));
|
|
795
|
+
const duration = Date.now() - startTime;
|
|
796
|
+
const modelInfo = this.getModelInfo(modelRef.model);
|
|
797
|
+
// Calculate cost - AI SDK v6 uses usage.totalTokens and similar
|
|
798
|
+
const promptTokens = result.usage?.promptTokens ??
|
|
799
|
+
result.usage?.inputTokens ?? 0;
|
|
800
|
+
const completionTokens = result.usage?.completionTokens ??
|
|
801
|
+
result.usage?.outputTokens ?? 0;
|
|
802
|
+
const cost = modelInfo
|
|
803
|
+
? (promptTokens / 1_000_000) * modelInfo.costPer1MInput +
|
|
804
|
+
(completionTokens / 1_000_000) * modelInfo.costPer1MOutput
|
|
805
|
+
: 0;
|
|
806
|
+
return {
|
|
807
|
+
id: randomUUID(),
|
|
808
|
+
provider: modelRef.provider,
|
|
809
|
+
model: modelRef.model,
|
|
810
|
+
content: result.text,
|
|
811
|
+
finishReason: result.finishReason === 'stop' ? 'stop' : 'length',
|
|
812
|
+
usage: {
|
|
813
|
+
promptTokens,
|
|
814
|
+
completionTokens,
|
|
815
|
+
totalTokens: promptTokens + completionTokens,
|
|
816
|
+
cost,
|
|
817
|
+
},
|
|
818
|
+
duration,
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
catch (error) {
|
|
822
|
+
logger.error(`[AIProvider] Chat error:`, error instanceof Error ? error : undefined);
|
|
823
|
+
throw error;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
// Streaming configuration for performance (configurable via env vars)
|
|
827
|
+
static CHUNK_BATCH_SIZE = parseInt(process.env.STREAM_CHUNK_BATCH_SIZE || '64', 10);
|
|
828
|
+
static CHUNK_BATCH_TIME_MS = parseInt(process.env.STREAM_CHUNK_BATCH_TIME_MS || '30', 10);
|
|
829
|
+
async chatStream(request, onChunk) {
|
|
830
|
+
const startTime = Date.now();
|
|
831
|
+
// Resolve model
|
|
832
|
+
const modelRef = request.model
|
|
833
|
+
? this.resolveModel(request.model)
|
|
834
|
+
: { provider: this.defaultProvider, model: 'llama3.2' };
|
|
835
|
+
const model = await this.getModel(modelRef.provider, modelRef.model);
|
|
836
|
+
// Convert messages
|
|
837
|
+
const messages = request.messages.map((msg) => ({
|
|
838
|
+
role: msg.role,
|
|
839
|
+
content: msg.content,
|
|
840
|
+
}));
|
|
841
|
+
if (request.systemPrompt) {
|
|
842
|
+
messages.unshift({ role: 'system', content: request.systemPrompt });
|
|
843
|
+
}
|
|
844
|
+
try {
|
|
845
|
+
const result = streamText({
|
|
846
|
+
model,
|
|
847
|
+
messages,
|
|
848
|
+
temperature: request.temperature ?? 0.7,
|
|
849
|
+
maxOutputTokens: request.maxTokens ?? 4096,
|
|
850
|
+
abortSignal: AbortSignal.timeout(AI_TIMEOUT_MS),
|
|
851
|
+
});
|
|
852
|
+
// Collect text with batched chunk emission for better performance
|
|
853
|
+
const textParts = [];
|
|
854
|
+
let chunkBuffer = '';
|
|
855
|
+
let lastFlushTime = Date.now();
|
|
856
|
+
for await (const chunk of result.textStream) {
|
|
857
|
+
textParts.push(chunk);
|
|
858
|
+
chunkBuffer += chunk;
|
|
859
|
+
const now = Date.now();
|
|
860
|
+
const shouldFlush = chunkBuffer.length >= AIProviderManager.CHUNK_BATCH_SIZE ||
|
|
861
|
+
now - lastFlushTime >= AIProviderManager.CHUNK_BATCH_TIME_MS;
|
|
862
|
+
if (shouldFlush && chunkBuffer) {
|
|
863
|
+
onChunk(chunkBuffer); // Non-blocking callback
|
|
864
|
+
chunkBuffer = '';
|
|
865
|
+
lastFlushTime = now;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
// Flush any remaining buffer
|
|
869
|
+
if (chunkBuffer) {
|
|
870
|
+
onChunk(chunkBuffer);
|
|
871
|
+
}
|
|
872
|
+
const fullText = textParts.join(''); // O(n) join instead of O(n²) concatenation
|
|
873
|
+
// Get usage (already available from stream result)
|
|
874
|
+
const usage = await result.usage;
|
|
875
|
+
const duration = Date.now() - startTime;
|
|
876
|
+
const modelInfo = this.getModelInfo(modelRef.model);
|
|
877
|
+
const promptTokens = usage?.promptTokens ??
|
|
878
|
+
usage?.inputTokens ?? 0;
|
|
879
|
+
const completionTokens = usage?.completionTokens ??
|
|
880
|
+
usage?.outputTokens ?? 0;
|
|
881
|
+
const cost = modelInfo
|
|
882
|
+
? (promptTokens / 1_000_000) * modelInfo.costPer1MInput +
|
|
883
|
+
(completionTokens / 1_000_000) * modelInfo.costPer1MOutput
|
|
884
|
+
: 0;
|
|
885
|
+
return {
|
|
886
|
+
id: randomUUID(),
|
|
887
|
+
provider: modelRef.provider,
|
|
888
|
+
model: modelRef.model,
|
|
889
|
+
content: fullText,
|
|
890
|
+
finishReason: 'stop',
|
|
891
|
+
usage: {
|
|
892
|
+
promptTokens,
|
|
893
|
+
completionTokens,
|
|
894
|
+
totalTokens: promptTokens + completionTokens,
|
|
895
|
+
cost,
|
|
896
|
+
},
|
|
897
|
+
duration,
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
catch (error) {
|
|
901
|
+
logger.error(`[AIProvider] Stream error:`, error instanceof Error ? error : undefined);
|
|
902
|
+
throw error;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Chat with tool calling support
|
|
907
|
+
* Handles tool detection and execution manually for better control
|
|
908
|
+
*/
|
|
909
|
+
async chatWithTools(request) {
|
|
910
|
+
const startTime = Date.now();
|
|
911
|
+
// Resolve model
|
|
912
|
+
const modelRef = request.model
|
|
913
|
+
? this.resolveModel(request.model)
|
|
914
|
+
: { provider: this.defaultProvider, model: 'llama3.2' };
|
|
915
|
+
const model = await this.getModel(modelRef.provider, modelRef.model);
|
|
916
|
+
// Check if model supports native tools
|
|
917
|
+
const toolSupport = this.modelSupportsTools(modelRef.model || modelRef.provider + '/' + modelRef.model);
|
|
918
|
+
const useNativeTools = toolSupport.supported && request.tools && request.tools.length > 0;
|
|
919
|
+
// Build system prompt (skip manual tool instructions if using native tools)
|
|
920
|
+
let systemPrompt = request.systemPrompt || '';
|
|
921
|
+
const toolResults = [];
|
|
922
|
+
const detectedToolCalls = [];
|
|
923
|
+
// Convert tools for native AI SDK usage
|
|
924
|
+
const aiSdkTools = {};
|
|
925
|
+
if (useNativeTools && request.tools) {
|
|
926
|
+
// Add a reinforcement prompt for local models to ensure they provide arguments
|
|
927
|
+
const toolReminders = request.tools.map(t => `- ${t.name}`).join(', ');
|
|
928
|
+
systemPrompt += `\n\nYou have access to these tools: ${toolReminders}. When using them, YOU MUST PROVIDE ALL REQUIRED ARGUMENTS (like 'url' for web_fetch, 'query' for web_search). Do not call tools with empty arguments.`;
|
|
929
|
+
request.tools.forEach((t) => {
|
|
930
|
+
// Convert Zod schema to JSON Schema for AI SDK v5+
|
|
931
|
+
const rawJsonSchema = zodToJsonSchema(t.parameters, {
|
|
932
|
+
$refStrategy: 'none',
|
|
933
|
+
});
|
|
934
|
+
aiSdkTools[t.name] = createTool({
|
|
935
|
+
description: t.description,
|
|
936
|
+
inputSchema: jsonSchema(asJsonSchemaInput(rawJsonSchema)),
|
|
937
|
+
});
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
else if (request.tools && request.tools.length > 0) {
|
|
941
|
+
// Fallback: Manual tool prompting
|
|
942
|
+
const toolDescriptions = request.tools.map((t) => {
|
|
943
|
+
const params = t.parameters;
|
|
944
|
+
const shape = params?._def?.shape ?? params?.shape ?? {};
|
|
945
|
+
const paramList = Object.entries(shape)
|
|
946
|
+
.map(([key, val]) => ` - ${key}: ${val?._def?.description || 'string'}`)
|
|
947
|
+
.join('\n');
|
|
948
|
+
return `**${t.name}**: ${t.description}\nParameters:\n${paramList}`;
|
|
949
|
+
}).join('\n\n');
|
|
950
|
+
systemPrompt += `\n\n## Available Tools\n\nYou can call the following tools by responding with a JSON block in this exact format:\n\`\`\`tool\n{"tool": "toolName", "args": {"param1": "value1"}}\n\`\`\`\n\nExample:\n\`\`\`tool\n{"tool": "web_search", "args": {"query": "latest typescript features"}}\n\`\`\`\n\n${toolDescriptions}\n\nWhen you need to use a tool, include the tool call in your response. After the tool executes, continue with your response.`;
|
|
951
|
+
}
|
|
952
|
+
// Convert messages to AI SDK format
|
|
953
|
+
const messages = request.messages.map((msg) => ({
|
|
954
|
+
role: msg.role,
|
|
955
|
+
content: msg.content,
|
|
956
|
+
}));
|
|
957
|
+
if (systemPrompt) {
|
|
958
|
+
messages.unshift({ role: 'system', content: systemPrompt });
|
|
959
|
+
}
|
|
960
|
+
try {
|
|
961
|
+
const result = await withRetry(() => generateText({
|
|
962
|
+
model,
|
|
963
|
+
messages,
|
|
964
|
+
temperature: request.temperature ?? 0.7,
|
|
965
|
+
maxOutputTokens: request.maxTokens ?? 4096,
|
|
966
|
+
tools: useNativeTools ? aiSdkTools : undefined,
|
|
967
|
+
abortSignal: AbortSignal.timeout(AI_TIMEOUT_MS),
|
|
968
|
+
}));
|
|
969
|
+
let finalContent = result.text;
|
|
970
|
+
// Handle native tool calls
|
|
971
|
+
if (useNativeTools && result.toolCalls && result.toolCalls.length > 0) {
|
|
972
|
+
for (const toolCall of result.toolCalls) {
|
|
973
|
+
if (request.executeTools) {
|
|
974
|
+
const args = toToolArguments(toolCall.input);
|
|
975
|
+
detectedToolCalls.push({
|
|
976
|
+
id: toolCall.toolCallId,
|
|
977
|
+
name: toolCall.toolName,
|
|
978
|
+
arguments: args,
|
|
979
|
+
});
|
|
980
|
+
// Execute the tool
|
|
981
|
+
const toolResult = await request.executeTools(toolCall.toolName, args);
|
|
982
|
+
toolResults.push({
|
|
983
|
+
toolCallId: toolCall.toolCallId,
|
|
984
|
+
toolName: toolCall.toolName,
|
|
985
|
+
result: toolResult,
|
|
986
|
+
});
|
|
987
|
+
// For native tools, we don't need to replace content text as the tool calls are structured
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
// Handle manual tool calls (fallback path)
|
|
992
|
+
else if (!useNativeTools) {
|
|
993
|
+
// Parse tool calls from the response (using pre-compiled regex)
|
|
994
|
+
TOOL_CALL_REGEX.lastIndex = 0; // Reset regex state
|
|
995
|
+
let match;
|
|
996
|
+
while ((match = TOOL_CALL_REGEX.exec(result.text)) !== null) {
|
|
997
|
+
try {
|
|
998
|
+
const toolCall = JSON.parse(match[1]);
|
|
999
|
+
if (toolCall.tool && request.executeTools) {
|
|
1000
|
+
const toolCallId = randomUUID();
|
|
1001
|
+
detectedToolCalls.push({
|
|
1002
|
+
id: toolCallId,
|
|
1003
|
+
name: toolCall.tool,
|
|
1004
|
+
arguments: toolCall.args || {},
|
|
1005
|
+
});
|
|
1006
|
+
// Execute the tool
|
|
1007
|
+
const toolResult = await request.executeTools(toolCall.tool, toolCall.args || {});
|
|
1008
|
+
toolResults.push({
|
|
1009
|
+
toolCallId,
|
|
1010
|
+
toolName: toolCall.tool,
|
|
1011
|
+
result: toolResult,
|
|
1012
|
+
});
|
|
1013
|
+
// Replace the tool call block with the result
|
|
1014
|
+
const resultMessage = getToolResultMessage(toolResult);
|
|
1015
|
+
finalContent = finalContent.replace(match[0], `\n✅ **${toolCall.tool}**: ${resultMessage}\n`);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
catch {
|
|
1019
|
+
// Invalid JSON, skip
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
const duration = Date.now() - startTime;
|
|
1024
|
+
const modelInfo = this.getModelInfo(modelRef.model);
|
|
1025
|
+
const promptTokens = result.usage?.promptTokens ??
|
|
1026
|
+
result.usage?.inputTokens ?? 0;
|
|
1027
|
+
const completionTokens = result.usage?.completionTokens ??
|
|
1028
|
+
result.usage?.outputTokens ?? 0;
|
|
1029
|
+
const cost = modelInfo
|
|
1030
|
+
? (promptTokens / 1_000_000) * modelInfo.costPer1MInput +
|
|
1031
|
+
(completionTokens / 1_000_000) * modelInfo.costPer1MOutput
|
|
1032
|
+
: 0;
|
|
1033
|
+
return {
|
|
1034
|
+
id: randomUUID(),
|
|
1035
|
+
provider: modelRef.provider,
|
|
1036
|
+
model: modelRef.model,
|
|
1037
|
+
content: finalContent,
|
|
1038
|
+
finishReason: detectedToolCalls.length > 0 ? 'tool-calls' : (result.finishReason === 'stop' ? 'stop' : 'length'),
|
|
1039
|
+
usage: {
|
|
1040
|
+
promptTokens,
|
|
1041
|
+
completionTokens,
|
|
1042
|
+
totalTokens: promptTokens + completionTokens,
|
|
1043
|
+
cost,
|
|
1044
|
+
},
|
|
1045
|
+
duration,
|
|
1046
|
+
toolCalls: detectedToolCalls.length > 0 ? detectedToolCalls : undefined,
|
|
1047
|
+
toolResults: toolResults.length > 0 ? toolResults : undefined,
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
catch (error) {
|
|
1051
|
+
logger.error(`[AIProvider] Chat with tools error:`, error instanceof Error ? error : undefined);
|
|
1052
|
+
throw error;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Check if a model supports native tool calling
|
|
1057
|
+
*/
|
|
1058
|
+
modelSupportsTools(modelOrAlias) {
|
|
1059
|
+
const modelRef = modelOrAlias
|
|
1060
|
+
? this.resolveModel(modelOrAlias)
|
|
1061
|
+
: { provider: this.defaultProvider, model: 'llama3.2' };
|
|
1062
|
+
// Azure OpenAI: All GPT-4, GPT-4o, and GPT-3.5-turbo models support function calling
|
|
1063
|
+
// We check provider first since Azure deployment names won't be in MODEL_CATALOG
|
|
1064
|
+
if (modelRef.provider === 'azure') {
|
|
1065
|
+
logger.debug(`[AIProvider] Azure model detected, assuming tool support`, {
|
|
1066
|
+
component: 'AIProvider',
|
|
1067
|
+
model: modelRef.model,
|
|
1068
|
+
provider: modelRef.provider,
|
|
1069
|
+
});
|
|
1070
|
+
return {
|
|
1071
|
+
supported: true,
|
|
1072
|
+
model: modelRef.model,
|
|
1073
|
+
provider: modelRef.provider,
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
const modelInfo = this.getModelInfo(modelRef.model);
|
|
1077
|
+
const supportsTools = modelInfo?.supportsTools ?? false;
|
|
1078
|
+
// Build recommendation for unsupported models
|
|
1079
|
+
let recommendation;
|
|
1080
|
+
if (!supportsTools) {
|
|
1081
|
+
// Find similar models that support tools
|
|
1082
|
+
const alternatives = MODEL_CATALOG.filter(m => m.supportsTools && m.provider !== 'ollama').slice(0, 3);
|
|
1083
|
+
if (alternatives.length > 0) {
|
|
1084
|
+
recommendation = `This model doesn't support tools. Try: ${alternatives.map(a => a.name).join(', ')}`;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
return {
|
|
1088
|
+
supported: supportsTools,
|
|
1089
|
+
model: modelRef.model,
|
|
1090
|
+
provider: modelRef.provider,
|
|
1091
|
+
recommendation,
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Chat with native tool calling using Vercel AI SDK
|
|
1096
|
+
* This uses the native tool() function for proper multi-step execution
|
|
1097
|
+
*
|
|
1098
|
+
* NOTE: If the model doesn't support native tool calling, tools will be
|
|
1099
|
+
* skipped and a warning will be included in the response.
|
|
1100
|
+
*/
|
|
1101
|
+
async chatWithNativeTools(request) {
|
|
1102
|
+
const startTime = Date.now();
|
|
1103
|
+
// Resolve model
|
|
1104
|
+
const modelRef = request.model
|
|
1105
|
+
? this.resolveModel(request.model)
|
|
1106
|
+
: { provider: this.defaultProvider, model: 'llama3.2' };
|
|
1107
|
+
const model = await this.getModel(modelRef.provider, modelRef.model);
|
|
1108
|
+
// Check if model supports native tool calling
|
|
1109
|
+
const toolSupport = this.modelSupportsTools(request.model);
|
|
1110
|
+
const shouldUseTools = toolSupport.supported && (request.tools?.length ?? 0) > 0;
|
|
1111
|
+
// Log warning if tools are requested but not supported
|
|
1112
|
+
if ((request.tools?.length ?? 0) > 0 && !toolSupport.supported) {
|
|
1113
|
+
logger.warn(`[AIProvider] Model ${modelRef.model} doesn't support native tool calling. Tools disabled.`, {
|
|
1114
|
+
component: 'AIProvider',
|
|
1115
|
+
model: modelRef.model,
|
|
1116
|
+
provider: modelRef.provider,
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
// Convert messages to AI SDK format
|
|
1120
|
+
const messages = toAiMessages(request.messages);
|
|
1121
|
+
// Add system prompt if provided
|
|
1122
|
+
if (request.systemPrompt) {
|
|
1123
|
+
messages.unshift({ role: 'system', content: request.systemPrompt });
|
|
1124
|
+
}
|
|
1125
|
+
// Track tool calls and results
|
|
1126
|
+
const toolCallHistory = [];
|
|
1127
|
+
const toolResultHistory = [];
|
|
1128
|
+
// Convert tool definitions to AI SDK format with execute handlers
|
|
1129
|
+
// Only build tools if the model supports them
|
|
1130
|
+
const tools = {};
|
|
1131
|
+
// Check if provider is Azure - needs normalized JSON Schema
|
|
1132
|
+
const isAzureProvider = modelRef.provider === 'azure';
|
|
1133
|
+
if (shouldUseTools) {
|
|
1134
|
+
for (const toolDef of request.tools || []) {
|
|
1135
|
+
// For Azure, convert Zod schemas to normalized JSON Schema
|
|
1136
|
+
// Azure has stricter schema validation requirements
|
|
1137
|
+
let inputSchema = toolDef.parameters;
|
|
1138
|
+
if (isAzureProvider && toolDef.parameters) {
|
|
1139
|
+
try {
|
|
1140
|
+
inputSchema = buildNativeToolSchema(toolDef.parameters, true);
|
|
1141
|
+
logger.debug(`[AIProvider] Normalized schema for Azure tool: ${toolDef.name}`, {
|
|
1142
|
+
component: 'AIProvider',
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
catch (schemaError) {
|
|
1146
|
+
logger.warn(`[AIProvider] Failed to normalize schema for ${toolDef.name}, using original`, {
|
|
1147
|
+
component: 'AIProvider',
|
|
1148
|
+
error: schemaError instanceof Error ? schemaError.message : String(schemaError),
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
// AI SDK v5+ uses inputSchema instead of parameters
|
|
1153
|
+
tools[toolDef.name] = createTool({
|
|
1154
|
+
description: toolDef.description,
|
|
1155
|
+
inputSchema,
|
|
1156
|
+
execute: async (args) => {
|
|
1157
|
+
const toolCallId = randomUUID();
|
|
1158
|
+
const toolArgs = toToolArguments(args);
|
|
1159
|
+
// Record the tool call
|
|
1160
|
+
toolCallHistory.push({
|
|
1161
|
+
id: toolCallId,
|
|
1162
|
+
name: toolDef.name,
|
|
1163
|
+
arguments: toolArgs,
|
|
1164
|
+
});
|
|
1165
|
+
// Execute through the provided handler (which goes through security)
|
|
1166
|
+
if (request.onToolCall) {
|
|
1167
|
+
try {
|
|
1168
|
+
const result = await request.onToolCall(toolDef.name, args, toolCallId);
|
|
1169
|
+
// Handle approval required case
|
|
1170
|
+
if (result.approvalRequired) {
|
|
1171
|
+
toolResultHistory.push({
|
|
1172
|
+
toolCallId,
|
|
1173
|
+
toolName: toolDef.name,
|
|
1174
|
+
result: {
|
|
1175
|
+
pending: true,
|
|
1176
|
+
approvalId: result.approvalId,
|
|
1177
|
+
message: `Awaiting approval for ${toolDef.name}`
|
|
1178
|
+
},
|
|
1179
|
+
approvalRequired: true,
|
|
1180
|
+
approvalId: result.approvalId,
|
|
1181
|
+
});
|
|
1182
|
+
return {
|
|
1183
|
+
pending: true,
|
|
1184
|
+
message: `Tool ${toolDef.name} requires approval. Approval ID: ${result.approvalId}`
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
// Record successful result
|
|
1188
|
+
toolResultHistory.push({
|
|
1189
|
+
toolCallId,
|
|
1190
|
+
toolName: toolDef.name,
|
|
1191
|
+
result: result.result,
|
|
1192
|
+
});
|
|
1193
|
+
return result.result;
|
|
1194
|
+
}
|
|
1195
|
+
catch (error) {
|
|
1196
|
+
const errorResult = {
|
|
1197
|
+
success: false,
|
|
1198
|
+
error: error instanceof Error ? error.message : 'Tool execution failed',
|
|
1199
|
+
};
|
|
1200
|
+
toolResultHistory.push({
|
|
1201
|
+
toolCallId,
|
|
1202
|
+
toolName: toolDef.name,
|
|
1203
|
+
result: errorResult,
|
|
1204
|
+
});
|
|
1205
|
+
return errorResult;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
// No handler provided, return placeholder
|
|
1209
|
+
return { error: 'No tool execution handler provided' };
|
|
1210
|
+
},
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
} // end if (shouldUseTools)
|
|
1214
|
+
try {
|
|
1215
|
+
const result = await withRetry(() => generateText({
|
|
1216
|
+
model,
|
|
1217
|
+
messages,
|
|
1218
|
+
tools: Object.keys(tools).length > 0 ? tools : undefined,
|
|
1219
|
+
temperature: request.temperature ?? 0.7,
|
|
1220
|
+
maxOutputTokens: request.maxTokens ?? 4096,
|
|
1221
|
+
maxSteps: request.maxToolRoundtrips ?? 5,
|
|
1222
|
+
abortSignal: AbortSignal.timeout(AI_TIMEOUT_MS),
|
|
1223
|
+
}));
|
|
1224
|
+
const duration = Date.now() - startTime;
|
|
1225
|
+
const modelInfo = this.getModelInfo(modelRef.model);
|
|
1226
|
+
// Calculate usage
|
|
1227
|
+
const promptTokens = result.usage?.promptTokens ??
|
|
1228
|
+
result.usage?.inputTokens ?? 0;
|
|
1229
|
+
const completionTokens = result.usage?.completionTokens ??
|
|
1230
|
+
result.usage?.outputTokens ?? 0;
|
|
1231
|
+
const cost = modelInfo
|
|
1232
|
+
? (promptTokens / 1_000_000) * modelInfo.costPer1MInput +
|
|
1233
|
+
(completionTokens / 1_000_000) * modelInfo.costPer1MOutput
|
|
1234
|
+
: 0;
|
|
1235
|
+
// Check if any tool calls require approval
|
|
1236
|
+
const pendingApprovals = toolResultHistory.filter(r => r.approvalRequired);
|
|
1237
|
+
return {
|
|
1238
|
+
id: randomUUID(),
|
|
1239
|
+
provider: modelRef.provider,
|
|
1240
|
+
model: modelRef.model,
|
|
1241
|
+
content: result.text,
|
|
1242
|
+
finishReason: pendingApprovals.length > 0
|
|
1243
|
+
? 'approval-required'
|
|
1244
|
+
: toolCallHistory.length > 0
|
|
1245
|
+
? 'tool-calls'
|
|
1246
|
+
: (result.finishReason === 'stop' ? 'stop' : 'length'),
|
|
1247
|
+
usage: {
|
|
1248
|
+
promptTokens,
|
|
1249
|
+
completionTokens,
|
|
1250
|
+
totalTokens: promptTokens + completionTokens,
|
|
1251
|
+
cost,
|
|
1252
|
+
},
|
|
1253
|
+
duration,
|
|
1254
|
+
toolCalls: toolCallHistory.length > 0 ? toolCallHistory : undefined,
|
|
1255
|
+
toolResults: toolResultHistory.length > 0 ? toolResultHistory : undefined,
|
|
1256
|
+
pendingApprovals: pendingApprovals.length > 0 ? pendingApprovals : undefined,
|
|
1257
|
+
steps: result.steps?.length ?? 1,
|
|
1258
|
+
// Tool support info for UI warnings
|
|
1259
|
+
toolSupport: {
|
|
1260
|
+
requested: (request.tools?.length ?? 0) > 0,
|
|
1261
|
+
supported: toolSupport.supported,
|
|
1262
|
+
used: shouldUseTools,
|
|
1263
|
+
recommendation: toolSupport.recommendation,
|
|
1264
|
+
},
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
catch (error) {
|
|
1268
|
+
logger.error(`[AIProvider] Native tool calling error:`, error instanceof Error ? error : undefined);
|
|
1269
|
+
throw error;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Stream chat with native tool calling
|
|
1274
|
+
*/
|
|
1275
|
+
async streamWithNativeTools(request, onChunk, onToolCall, onToolResult) {
|
|
1276
|
+
const startTime = Date.now();
|
|
1277
|
+
// Resolve model
|
|
1278
|
+
const modelRef = request.model
|
|
1279
|
+
? this.resolveModel(request.model)
|
|
1280
|
+
: { provider: this.defaultProvider, model: 'llama3.2' };
|
|
1281
|
+
const model = await this.getModel(modelRef.provider, modelRef.model);
|
|
1282
|
+
// Convert messages
|
|
1283
|
+
const messages = toAiMessages(request.messages);
|
|
1284
|
+
if (request.systemPrompt) {
|
|
1285
|
+
messages.unshift({ role: 'system', content: request.systemPrompt });
|
|
1286
|
+
}
|
|
1287
|
+
// Track tool calls
|
|
1288
|
+
const toolCallHistory = [];
|
|
1289
|
+
const toolResultHistory = [];
|
|
1290
|
+
// Build tools with execute handlers
|
|
1291
|
+
const tools = {};
|
|
1292
|
+
// Check if provider is Azure - needs normalized JSON Schema
|
|
1293
|
+
const isAzureProvider = modelRef.provider === 'azure';
|
|
1294
|
+
for (const toolDef of request.tools || []) {
|
|
1295
|
+
// For Azure, convert Zod schemas to normalized JSON Schema
|
|
1296
|
+
let inputSchema = toolDef.parameters;
|
|
1297
|
+
if (isAzureProvider && toolDef.parameters) {
|
|
1298
|
+
try {
|
|
1299
|
+
inputSchema = buildNativeToolSchema(toolDef.parameters, true);
|
|
1300
|
+
}
|
|
1301
|
+
catch {
|
|
1302
|
+
// Fall back to original schema
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
// AI SDK v5+ uses inputSchema instead of parameters
|
|
1306
|
+
tools[toolDef.name] = createTool({
|
|
1307
|
+
description: toolDef.description,
|
|
1308
|
+
inputSchema,
|
|
1309
|
+
execute: async (args) => {
|
|
1310
|
+
const toolCallId = randomUUID();
|
|
1311
|
+
const toolArgs = toToolArguments(args);
|
|
1312
|
+
toolCallHistory.push({
|
|
1313
|
+
id: toolCallId,
|
|
1314
|
+
name: toolDef.name,
|
|
1315
|
+
arguments: toolArgs,
|
|
1316
|
+
});
|
|
1317
|
+
// Notify about tool call
|
|
1318
|
+
onToolCall?.(toolDef.name, args);
|
|
1319
|
+
if (request.onToolCall) {
|
|
1320
|
+
try {
|
|
1321
|
+
const result = await request.onToolCall(toolDef.name, args, toolCallId);
|
|
1322
|
+
if (result.approvalRequired) {
|
|
1323
|
+
toolResultHistory.push({
|
|
1324
|
+
toolCallId,
|
|
1325
|
+
toolName: toolDef.name,
|
|
1326
|
+
result: { pending: true, approvalId: result.approvalId },
|
|
1327
|
+
approvalRequired: true,
|
|
1328
|
+
approvalId: result.approvalId,
|
|
1329
|
+
});
|
|
1330
|
+
onToolResult?.(toolDef.name, { pending: true, approvalRequired: true });
|
|
1331
|
+
return { pending: true, message: `Awaiting approval` };
|
|
1332
|
+
}
|
|
1333
|
+
toolResultHistory.push({
|
|
1334
|
+
toolCallId,
|
|
1335
|
+
toolName: toolDef.name,
|
|
1336
|
+
result: result.result,
|
|
1337
|
+
});
|
|
1338
|
+
onToolResult?.(toolDef.name, result.result);
|
|
1339
|
+
return result.result;
|
|
1340
|
+
}
|
|
1341
|
+
catch (error) {
|
|
1342
|
+
const errorResult = {
|
|
1343
|
+
success: false,
|
|
1344
|
+
error: error instanceof Error ? error.message : 'Failed'
|
|
1345
|
+
};
|
|
1346
|
+
toolResultHistory.push({
|
|
1347
|
+
toolCallId,
|
|
1348
|
+
toolName: toolDef.name,
|
|
1349
|
+
result: errorResult,
|
|
1350
|
+
});
|
|
1351
|
+
onToolResult?.(toolDef.name, errorResult);
|
|
1352
|
+
return errorResult;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
return { error: 'No handler' };
|
|
1356
|
+
},
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
try {
|
|
1360
|
+
const result = streamText({
|
|
1361
|
+
model,
|
|
1362
|
+
messages,
|
|
1363
|
+
tools: Object.keys(tools).length > 0 ? tools : undefined,
|
|
1364
|
+
temperature: request.temperature ?? 0.7,
|
|
1365
|
+
maxOutputTokens: request.maxTokens ?? 4096,
|
|
1366
|
+
maxSteps: request.maxToolRoundtrips ?? 5,
|
|
1367
|
+
abortSignal: AbortSignal.timeout(AI_TIMEOUT_MS),
|
|
1368
|
+
});
|
|
1369
|
+
let fullText = '';
|
|
1370
|
+
for await (const chunk of result.textStream) {
|
|
1371
|
+
fullText += chunk;
|
|
1372
|
+
onChunk(chunk);
|
|
1373
|
+
}
|
|
1374
|
+
const usage = await result.usage;
|
|
1375
|
+
const duration = Date.now() - startTime;
|
|
1376
|
+
const modelInfo = this.getModelInfo(modelRef.model);
|
|
1377
|
+
const promptTokens = usage?.promptTokens ??
|
|
1378
|
+
usage?.inputTokens ?? 0;
|
|
1379
|
+
const completionTokens = usage?.completionTokens ??
|
|
1380
|
+
usage?.outputTokens ?? 0;
|
|
1381
|
+
const cost = modelInfo
|
|
1382
|
+
? (promptTokens / 1_000_000) * modelInfo.costPer1MInput +
|
|
1383
|
+
(completionTokens / 1_000_000) * modelInfo.costPer1MOutput
|
|
1384
|
+
: 0;
|
|
1385
|
+
const pendingApprovals = toolResultHistory.filter(r => r.approvalRequired);
|
|
1386
|
+
return {
|
|
1387
|
+
id: randomUUID(),
|
|
1388
|
+
provider: modelRef.provider,
|
|
1389
|
+
model: modelRef.model,
|
|
1390
|
+
content: fullText,
|
|
1391
|
+
finishReason: pendingApprovals.length > 0
|
|
1392
|
+
? 'approval-required'
|
|
1393
|
+
: toolCallHistory.length > 0
|
|
1394
|
+
? 'tool-calls'
|
|
1395
|
+
: 'stop',
|
|
1396
|
+
usage: {
|
|
1397
|
+
promptTokens,
|
|
1398
|
+
completionTokens,
|
|
1399
|
+
totalTokens: promptTokens + completionTokens,
|
|
1400
|
+
cost,
|
|
1401
|
+
},
|
|
1402
|
+
duration,
|
|
1403
|
+
toolCalls: toolCallHistory.length > 0 ? toolCallHistory : undefined,
|
|
1404
|
+
toolResults: toolResultHistory.length > 0 ? toolResultHistory : undefined,
|
|
1405
|
+
pendingApprovals: pendingApprovals.length > 0 ? pendingApprovals : undefined,
|
|
1406
|
+
steps: 1,
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
catch (error) {
|
|
1410
|
+
logger.error(`[AIProvider] Stream with tools error:`, error instanceof Error ? error : undefined);
|
|
1411
|
+
throw error;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
async healthCheck(provider) {
|
|
1415
|
+
const results = [];
|
|
1416
|
+
const providers = provider
|
|
1417
|
+
? [provider]
|
|
1418
|
+
: this.getConfiguredProviders();
|
|
1419
|
+
for (const p of providers) {
|
|
1420
|
+
const start = Date.now();
|
|
1421
|
+
try {
|
|
1422
|
+
if (p === 'ollama') {
|
|
1423
|
+
// Check Ollama server
|
|
1424
|
+
const config = this.configs.get('ollama');
|
|
1425
|
+
const baseUrl = config?.baseUrl || 'http://localhost:11434';
|
|
1426
|
+
const response = await fetch(`${baseUrl}/api/tags`, {
|
|
1427
|
+
signal: AbortSignal.timeout(5000),
|
|
1428
|
+
});
|
|
1429
|
+
if (response.ok) {
|
|
1430
|
+
results.push({
|
|
1431
|
+
provider: p,
|
|
1432
|
+
healthy: true,
|
|
1433
|
+
message: 'Ollama server is running',
|
|
1434
|
+
latencyMs: Date.now() - start,
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
else {
|
|
1438
|
+
results.push({
|
|
1439
|
+
provider: p,
|
|
1440
|
+
healthy: false,
|
|
1441
|
+
message: `Ollama returned ${response.status}`,
|
|
1442
|
+
latencyMs: Date.now() - start,
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
else if (p === 'groq') {
|
|
1447
|
+
// Check Groq API
|
|
1448
|
+
const config = this.configs.get('groq');
|
|
1449
|
+
if (config?.apiKey) {
|
|
1450
|
+
try {
|
|
1451
|
+
const response = await fetch('https://api.groq.com/openai/v1/models', {
|
|
1452
|
+
headers: { Authorization: `Bearer ${config.apiKey}` },
|
|
1453
|
+
signal: AbortSignal.timeout(5000),
|
|
1454
|
+
});
|
|
1455
|
+
results.push({
|
|
1456
|
+
provider: p,
|
|
1457
|
+
healthy: response.ok,
|
|
1458
|
+
message: response.ok ? 'Groq API is accessible' : `Groq returned ${response.status}`,
|
|
1459
|
+
latencyMs: Date.now() - start,
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
catch {
|
|
1463
|
+
results.push({
|
|
1464
|
+
provider: p,
|
|
1465
|
+
healthy: false,
|
|
1466
|
+
message: 'Groq API unreachable',
|
|
1467
|
+
latencyMs: Date.now() - start,
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
else {
|
|
1472
|
+
results.push({
|
|
1473
|
+
provider: p,
|
|
1474
|
+
healthy: false,
|
|
1475
|
+
message: 'Groq not configured',
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
else if (p === 'azure') {
|
|
1480
|
+
// Azure OpenAI - check if configured (supports both baseUrl and resourceName modes)
|
|
1481
|
+
const config = this.configs.get('azure');
|
|
1482
|
+
const hasValidConfig = !!(config?.apiKey && (config?.baseUrl || config?.resourceName));
|
|
1483
|
+
results.push({
|
|
1484
|
+
provider: p,
|
|
1485
|
+
healthy: hasValidConfig,
|
|
1486
|
+
message: hasValidConfig
|
|
1487
|
+
? `Azure OpenAI configured${config?.baseUrl ? ` (${config.baseUrl})` : config?.resourceName ? ` (${config.resourceName})` : ''}`
|
|
1488
|
+
: 'Azure OpenAI not configured',
|
|
1489
|
+
latencyMs: Date.now() - start,
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
else {
|
|
1493
|
+
// For cloud providers, we assume they're healthy if configured
|
|
1494
|
+
results.push({
|
|
1495
|
+
provider: p,
|
|
1496
|
+
healthy: this.isConfigured(p),
|
|
1497
|
+
message: this.isConfigured(p)
|
|
1498
|
+
? `${p} is configured`
|
|
1499
|
+
: `${p} not configured`,
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
catch (error) {
|
|
1504
|
+
results.push({
|
|
1505
|
+
provider: p,
|
|
1506
|
+
healthy: false,
|
|
1507
|
+
message: error instanceof Error ? error.message : 'Health check failed',
|
|
1508
|
+
latencyMs: Date.now() - start,
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return results;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
// === Singleton Export ===
|
|
1516
|
+
export const aiProvider = new AIProviderManager();
|
|
1517
|
+
export async function chat(prompt, options) {
|
|
1518
|
+
const response = await aiProvider.chat({
|
|
1519
|
+
messages: [
|
|
1520
|
+
{
|
|
1521
|
+
id: randomUUID(),
|
|
1522
|
+
role: 'user',
|
|
1523
|
+
content: prompt,
|
|
1524
|
+
timestamp: new Date().toISOString(),
|
|
1525
|
+
},
|
|
1526
|
+
],
|
|
1527
|
+
model: options?.model,
|
|
1528
|
+
systemPrompt: options?.systemPrompt,
|
|
1529
|
+
temperature: options?.temperature,
|
|
1530
|
+
});
|
|
1531
|
+
return response.content;
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Stream chat function
|
|
1535
|
+
*/
|
|
1536
|
+
export async function chatStream(prompt, onChunk, options) {
|
|
1537
|
+
const response = await aiProvider.chatStream({
|
|
1538
|
+
messages: [
|
|
1539
|
+
{
|
|
1540
|
+
id: randomUUID(),
|
|
1541
|
+
role: 'user',
|
|
1542
|
+
content: prompt,
|
|
1543
|
+
timestamp: new Date().toISOString(),
|
|
1544
|
+
},
|
|
1545
|
+
],
|
|
1546
|
+
model: options?.model,
|
|
1547
|
+
systemPrompt: options?.systemPrompt,
|
|
1548
|
+
temperature: options?.temperature,
|
|
1549
|
+
}, onChunk);
|
|
1550
|
+
return response.content;
|
|
1551
|
+
}
|
|
1552
|
+
//# sourceMappingURL=ai-sdk.js.map
|