fix(ai-providers): change generateObject mode from 'tool' to 'auto' for better provider compatibility
Fixes Perplexity research role failing with 'tool-mode object generation' error The hardcoded 'tool' mode was incompatible with providers like Perplexity that support structured JSON output but not function calling/tool use Using 'auto' mode allows the AI SDK to choose the best approach for each provider
This commit is contained in:
37
.taskmaster/tasks/task_096.txt
Normal file
37
.taskmaster/tasks/task_096.txt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Task ID: 96
|
||||||
|
# Title: Create Export Command for On-Demand Task File and PDF Generation
|
||||||
|
# Status: pending
|
||||||
|
# Dependencies: 2, 4, 95
|
||||||
|
# Priority: medium
|
||||||
|
# Description: Develop an 'export' CLI command that generates task files and comprehensive PDF exports on-demand, replacing automatic file generation and providing users with flexible export options.
|
||||||
|
# Details:
|
||||||
|
Implement a new 'export' command in the CLI that supports two primary modes: (1) generating individual task files on-demand (superseding the current automatic generation system), and (2) producing a comprehensive PDF export. The PDF should include: a first page with the output of 'tm list --with-subtasks', followed by individual pages for each task (using 'tm show <task_id>') and each subtask (using 'tm show <subtask_id>'). Integrate PDF generation using a robust library (e.g., pdfkit, Puppeteer, or jsPDF) to ensure high-quality output and proper pagination. Refactor or disable any existing automatic file generation logic to avoid performance overhead. Ensure the command supports flexible output paths and options for exporting only files, only PDF, or both. Update documentation and help output to reflect the new export capabilities. Consider concurrency and error handling for large projects. Ensure the export process is efficient and does not block the main CLI thread unnecessarily.
|
||||||
|
|
||||||
|
# Test Strategy:
|
||||||
|
1. Run the 'export' command with various options and verify that task files are generated only on-demand, not automatically. 2. Generate a PDF export and confirm that the first page contains the correct 'tm list --with-subtasks' output, and that each subsequent page accurately reflects the output of 'tm show <task_id>' and 'tm show <subtask_id>' for all tasks and subtasks. 3. Test exporting in projects with large numbers of tasks and subtasks to ensure performance and correctness. 4. Attempt exports with invalid paths or missing data to verify robust error handling. 5. Confirm that no automatic file generation occurs during normal task operations. 6. Review CLI help output and documentation for accuracy regarding the new export functionality.
|
||||||
|
|
||||||
|
# Subtasks:
|
||||||
|
## 1. Remove Automatic Task File Generation from Task Operations [pending]
|
||||||
|
### Dependencies: None
|
||||||
|
### Description: Eliminate all calls to generateTaskFiles() from task operations such as add-task, remove-task, set-status, and similar commands to prevent unnecessary performance overhead.
|
||||||
|
### Details:
|
||||||
|
Audit the codebase for any automatic invocations of generateTaskFiles() and remove or refactor them to ensure task files are not generated automatically during task operations.
|
||||||
|
|
||||||
|
## 2. Implement Export Command Infrastructure with On-Demand Task File Generation [pending]
|
||||||
|
### Dependencies: 96.1
|
||||||
|
### Description: Develop the CLI 'export' command infrastructure, enabling users to generate task files on-demand by invoking the preserved generateTaskFiles function only when requested.
|
||||||
|
### Details:
|
||||||
|
Create the export command with options for output paths and modes (files, PDF, or both). Ensure generateTaskFiles is only called within this command and not elsewhere.
|
||||||
|
|
||||||
|
## 3. Implement Comprehensive PDF Export Functionality [pending]
|
||||||
|
### Dependencies: 96.2
|
||||||
|
### Description: Add PDF export capability to the export command, generating a structured PDF with a first page listing all tasks and subtasks, followed by individual pages for each task and subtask, using a robust PDF library.
|
||||||
|
### Details:
|
||||||
|
Integrate a PDF generation library (e.g., pdfkit, Puppeteer, or jsPDF). Ensure the PDF includes the output of 'tm list --with-subtasks' on the first page, and uses 'tm show <task_id>' and 'tm show <subtask_id>' for subsequent pages. Handle pagination, concurrency, and error handling for large projects.
|
||||||
|
|
||||||
|
## 4. Update Documentation, Tests, and CLI Help for Export Workflow [pending]
|
||||||
|
### Dependencies: 96.2, 96.3
|
||||||
|
### Description: Revise all relevant documentation, automated tests, and CLI help output to reflect the new export-based workflow and available options.
|
||||||
|
### Details:
|
||||||
|
Update user guides, README files, and CLI help text. Add or modify tests to cover the new export command and its options. Ensure all documentation accurately describes the new workflow and usage.
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
252
package-lock.json
generated
252
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "task-master-ai",
|
"name": "task-master-ai",
|
||||||
"version": "0.16.1",
|
"version": "0.16.2-rc.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "task-master-ai",
|
"name": "task-master-ai",
|
||||||
"version": "0.16.1",
|
"version": "0.16.2-rc.0",
|
||||||
"license": "MIT WITH Commons-Clause",
|
"license": "MIT WITH Commons-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/amazon-bedrock": "^2.2.9",
|
"@ai-sdk/amazon-bedrock": "^2.2.9",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"fastmcp": "^1.20.5",
|
"fastmcp": "^2.2.2",
|
||||||
"figlet": "^1.8.0",
|
"figlet": "^1.8.0",
|
||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
"gradient-string": "^3.0.0",
|
"gradient-string": "^3.0.0",
|
||||||
@@ -3593,18 +3593,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@modelcontextprotocol/sdk": {
|
"node_modules/@modelcontextprotocol/sdk": {
|
||||||
"version": "1.8.0",
|
"version": "1.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.1.tgz",
|
||||||
"integrity": "sha512-e06W7SwrontJDHwCawNO5SGxG+nU9AAx+jpHHZqGl/WrDBdWOpvirC+s58VpJTB5QemI4jTRcjWT4Pt3Q1NPQQ==",
|
"integrity": "sha512-KG1CZhZfWg+u8pxeM/mByJDScJSrjjxLc8fwQqbsS8xCjBmQfMNEBTotYdNanKekepnfRI85GtgQlctLFpcYPw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"ajv": "^6.12.6",
|
||||||
"content-type": "^1.0.5",
|
"content-type": "^1.0.5",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"cross-spawn": "^7.0.3",
|
"cross-spawn": "^7.0.5",
|
||||||
"eventsource": "^3.0.2",
|
"eventsource": "^3.0.2",
|
||||||
"express": "^5.0.1",
|
"express": "^5.0.1",
|
||||||
"express-rate-limit": "^7.5.0",
|
"express-rate-limit": "^7.5.0",
|
||||||
"pkce-challenge": "^4.1.0",
|
"pkce-challenge": "^5.0.0",
|
||||||
"raw-body": "^3.0.0",
|
"raw-body": "^3.0.0",
|
||||||
"zod": "^3.23.8",
|
"zod": "^3.23.8",
|
||||||
"zod-to-json-schema": "^3.24.1"
|
"zod-to-json-schema": "^3.24.1"
|
||||||
@@ -3668,84 +3669,45 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@modelcontextprotocol/sdk/node_modules/express": {
|
"node_modules/@modelcontextprotocol/sdk/node_modules/express": {
|
||||||
"version": "5.0.1",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
||||||
"integrity": "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==",
|
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "^2.0.0",
|
"accepts": "^2.0.0",
|
||||||
"body-parser": "^2.0.1",
|
"body-parser": "^2.2.0",
|
||||||
"content-disposition": "^1.0.0",
|
"content-disposition": "^1.0.0",
|
||||||
"content-type": "~1.0.4",
|
"content-type": "^1.0.5",
|
||||||
"cookie": "0.7.1",
|
"cookie": "^0.7.1",
|
||||||
"cookie-signature": "^1.2.1",
|
"cookie-signature": "^1.2.1",
|
||||||
"debug": "4.3.6",
|
"debug": "^4.4.0",
|
||||||
"depd": "2.0.0",
|
"encodeurl": "^2.0.0",
|
||||||
"encodeurl": "~2.0.0",
|
"escape-html": "^1.0.3",
|
||||||
"escape-html": "~1.0.3",
|
"etag": "^1.8.1",
|
||||||
"etag": "~1.8.1",
|
"finalhandler": "^2.1.0",
|
||||||
"finalhandler": "^2.0.0",
|
"fresh": "^2.0.0",
|
||||||
"fresh": "2.0.0",
|
"http-errors": "^2.0.0",
|
||||||
"http-errors": "2.0.0",
|
|
||||||
"merge-descriptors": "^2.0.0",
|
"merge-descriptors": "^2.0.0",
|
||||||
"methods": "~1.1.2",
|
|
||||||
"mime-types": "^3.0.0",
|
"mime-types": "^3.0.0",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "^2.4.1",
|
||||||
"once": "1.4.0",
|
"once": "^1.4.0",
|
||||||
"parseurl": "~1.3.3",
|
"parseurl": "^1.3.3",
|
||||||
"proxy-addr": "~2.0.7",
|
"proxy-addr": "^2.0.7",
|
||||||
"qs": "6.13.0",
|
"qs": "^6.14.0",
|
||||||
"range-parser": "~1.2.1",
|
"range-parser": "^1.2.1",
|
||||||
"router": "^2.0.0",
|
"router": "^2.2.0",
|
||||||
"safe-buffer": "5.2.1",
|
|
||||||
"send": "^1.1.0",
|
"send": "^1.1.0",
|
||||||
"serve-static": "^2.1.0",
|
"serve-static": "^2.2.0",
|
||||||
"setprototypeof": "1.2.0",
|
"statuses": "^2.0.1",
|
||||||
"statuses": "2.0.1",
|
"type-is": "^2.0.1",
|
||||||
"type-is": "^2.0.0",
|
"vary": "^1.1.2"
|
||||||
"utils-merge": "1.0.1",
|
|
||||||
"vary": "~1.1.2"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/debug": {
|
|
||||||
"version": "4.3.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
|
||||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ms": "2.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"supports-color": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/ms": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/qs": {
|
|
||||||
"version": "6.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
|
||||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"side-channel": "^1.0.6"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6"
|
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": {
|
"node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": {
|
||||||
@@ -4640,6 +4602,12 @@
|
|||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@standard-schema/spec": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@tokenizer/inflate": {
|
"node_modules/@tokenizer/inflate": {
|
||||||
"version": "0.2.7",
|
"version": "0.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz",
|
||||||
@@ -4884,6 +4852,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ajv": {
|
||||||
|
"version": "6.12.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
"json-schema-traverse": "^0.4.1",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-align": {
|
"node_modules/ansi-align": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
||||||
@@ -6432,9 +6416,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eventsource": {
|
"node_modules/eventsource": {
|
||||||
"version": "3.0.6",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
|
||||||
"integrity": "sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==",
|
"integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eventsource-parser": "^3.0.1"
|
"eventsource-parser": "^3.0.1"
|
||||||
@@ -6636,6 +6620,12 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-deep-equal": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||||
@@ -6657,7 +6647,6 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-safe-stringify": {
|
"node_modules/fast-safe-stringify": {
|
||||||
@@ -6690,22 +6679,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fastmcp": {
|
"node_modules/fastmcp": {
|
||||||
"version": "1.20.5",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/fastmcp/-/fastmcp-1.20.5.tgz",
|
"resolved": "https://registry.npmjs.org/fastmcp/-/fastmcp-2.2.2.tgz",
|
||||||
"integrity": "sha512-jwcPgMF9bcE9qsEG82YMlAG26/n5CSYsr95e60ntqWWd+3kgTBbUIasB3HfpqHLTNaQuoX6/jl18fpDcybBjcQ==",
|
"integrity": "sha512-V6qEfOnABo7lDrwHqZQhCYd52KXzK85/ipllmUyaos8WLAjygP9NuuKcm1kiEWa0jjsFxe2kf/Y+T4PRE+0rEw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.6.0",
|
"@modelcontextprotocol/sdk": "^1.10.2",
|
||||||
|
"@standard-schema/spec": "^1.0.0",
|
||||||
"execa": "^9.5.2",
|
"execa": "^9.5.2",
|
||||||
"file-type": "^20.3.0",
|
"file-type": "^20.4.1",
|
||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
"mcp-proxy": "^2.10.4",
|
"mcp-proxy": "^3.0.3",
|
||||||
"strict-event-emitter-types": "^2.0.0",
|
"strict-event-emitter-types": "^2.0.0",
|
||||||
"undici": "^7.4.0",
|
"undici": "^7.8.0",
|
||||||
"uri-templates": "^0.2.0",
|
"uri-templates": "^0.2.0",
|
||||||
|
"xsschema": "0.3.0-beta.1",
|
||||||
"yargs": "^17.7.2",
|
"yargs": "^17.7.2",
|
||||||
"zod": "^3.24.2",
|
"zod": "^3.25.12",
|
||||||
"zod-to-json-schema": "^3.24.3"
|
"zod-to-json-schema": "^3.24.5"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"fastmcp": "dist/bin/fastmcp.js"
|
"fastmcp": "dist/bin/fastmcp.js"
|
||||||
@@ -9104,6 +9095,12 @@
|
|||||||
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
|
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
|
||||||
"license": "(AFL-2.1 OR BSD-3-Clause)"
|
"license": "(AFL-2.1 OR BSD-3-Clause)"
|
||||||
},
|
},
|
||||||
|
"node_modules/json-schema-traverse": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/json5": {
|
"node_modules/json5": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||||
@@ -9383,19 +9380,31 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mcp-proxy": {
|
"node_modules/mcp-proxy": {
|
||||||
"version": "2.12.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/mcp-proxy/-/mcp-proxy-2.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/mcp-proxy/-/mcp-proxy-3.3.0.tgz",
|
||||||
"integrity": "sha512-hL2Y6EtK7vkgAOZxOQe9M4Z9g5xEnvR4ZYBKqFi/5tjhz/1jyNEz5NL87Uzv46k8iZQPVNEof/T6arEooBU5bQ==",
|
"integrity": "sha512-xyFKQEZ64HC7lxScBHjb5fxiPoyJjjkPhwH5hWUT0oL/ttCpMGZDJrYZRGFKVJiLLkrZPAkHnMGkI+WMlyD/cg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.6.0",
|
"@modelcontextprotocol/sdk": "^1.11.4",
|
||||||
"eventsource": "^3.0.5",
|
"eventsource": "^4.0.0",
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"mcp-proxy": "dist/bin/mcp-proxy.js"
|
"mcp-proxy": "dist/bin/mcp-proxy.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mcp-proxy/node_modules/eventsource": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-fvIkb9qZzdMxgZrEQDyll+9oJsyaVvY92I2Re+qK0qEJ+w5s0X3dtz+M0VAPOjP1gtU3iqWyjQ0G3nvd5CLZ2g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"eventsource-parser": "^3.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/media-typer": {
|
"node_modules/media-typer": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
@@ -10085,9 +10094,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pkce-challenge": {
|
"node_modules/pkce-challenge": {
|
||||||
"version": "4.1.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
|
||||||
"integrity": "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==",
|
"integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.20.0"
|
"node": ">=16.20.0"
|
||||||
@@ -11319,9 +11328,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici": {
|
"node_modules/undici": {
|
||||||
"version": "7.6.0",
|
"version": "7.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici/-/undici-7.10.0.tgz",
|
||||||
"integrity": "sha512-gaFsbThjrDGvAaD670r81RZro/s6H2PVZF640Qn0p5kZK+/rim7/mmyfp2W7VB5vOMaFM8vuFBJUaMlaZTYHlA==",
|
"integrity": "sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.18.1"
|
"node": ">=20.18.1"
|
||||||
@@ -11395,6 +11404,15 @@
|
|||||||
"browserslist": ">= 4.21.0"
|
"browserslist": ">= 4.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/uri-js": {
|
||||||
|
"version": "4.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||||
|
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/uri-templates": {
|
"node_modules/uri-templates": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/uri-templates/-/uri-templates-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/uri-templates/-/uri-templates-0.2.0.tgz",
|
||||||
@@ -11605,6 +11623,40 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xsschema": {
|
||||||
|
"version": "0.3.0-beta.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xsschema/-/xsschema-0.3.0-beta.1.tgz",
|
||||||
|
"integrity": "sha512-Z7ZlPKLTc8iUKVfic0Lr66NB777wJqZl3JVLIy1vaNxx6NNTuylYm4wbK78Sgg7kHwaPRqFnuT4IliQM1sDxvg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@valibot/to-json-schema": "^1.0.0",
|
||||||
|
"arktype": "^2.1.16",
|
||||||
|
"effect": "^3.14.5",
|
||||||
|
"sury": "^10.0.0-rc",
|
||||||
|
"zod": "^3.25.0",
|
||||||
|
"zod-to-json-schema": "^3.24.5"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@valibot/to-json-schema": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"arktype": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"effect": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sury": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"zod": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"zod-to-json-schema": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "5.0.8",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||||
@@ -11734,9 +11786,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/zod": {
|
"node_modules/zod": {
|
||||||
"version": "3.24.2",
|
"version": "3.25.56",
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.56.tgz",
|
||||||
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==",
|
"integrity": "sha512-rd6eEF3BTNvQnR2e2wwolfTmUTnp70aUTqr0oaGbHifzC3BKJsoV+Gat8vxUMR1hwOKBs6El+qWehrHbCpW6SQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ import {
|
|||||||
getAzureBaseURL,
|
getAzureBaseURL,
|
||||||
getBedrockBaseURL,
|
getBedrockBaseURL,
|
||||||
getVertexProjectId,
|
getVertexProjectId,
|
||||||
getVertexLocation
|
getVertexLocation,
|
||||||
} from './config-manager.js';
|
} from "./config-manager.js";
|
||||||
import { log, findProjectRoot, resolveEnvVariable } from './utils.js';
|
import { log, findProjectRoot, resolveEnvVariable } from "./utils.js";
|
||||||
|
|
||||||
// Import provider classes
|
// Import provider classes
|
||||||
import {
|
import {
|
||||||
@@ -39,8 +39,8 @@ import {
|
|||||||
OllamaAIProvider,
|
OllamaAIProvider,
|
||||||
BedrockAIProvider,
|
BedrockAIProvider,
|
||||||
AzureProvider,
|
AzureProvider,
|
||||||
VertexAIProvider
|
VertexAIProvider,
|
||||||
} from '../../src/ai-providers/index.js';
|
} from "../../src/ai-providers/index.js";
|
||||||
|
|
||||||
// Create provider instances
|
// Create provider instances
|
||||||
const PROVIDERS = {
|
const PROVIDERS = {
|
||||||
@@ -53,36 +53,36 @@ const PROVIDERS = {
|
|||||||
ollama: new OllamaAIProvider(),
|
ollama: new OllamaAIProvider(),
|
||||||
bedrock: new BedrockAIProvider(),
|
bedrock: new BedrockAIProvider(),
|
||||||
azure: new AzureProvider(),
|
azure: new AzureProvider(),
|
||||||
vertex: new VertexAIProvider()
|
vertex: new VertexAIProvider(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function to get cost for a specific model
|
// Helper function to get cost for a specific model
|
||||||
function _getCostForModel(providerName, modelId) {
|
function _getCostForModel(providerName, modelId) {
|
||||||
if (!MODEL_MAP || !MODEL_MAP[providerName]) {
|
if (!MODEL_MAP || !MODEL_MAP[providerName]) {
|
||||||
log(
|
log(
|
||||||
'warn',
|
"warn",
|
||||||
`Provider "${providerName}" not found in MODEL_MAP. Cannot determine cost for model ${modelId}.`
|
`Provider "${providerName}" not found in MODEL_MAP. Cannot determine cost for model ${modelId}.`
|
||||||
);
|
);
|
||||||
return { inputCost: 0, outputCost: 0, currency: 'USD' }; // Default to zero cost
|
return { inputCost: 0, outputCost: 0, currency: "USD" }; // Default to zero cost
|
||||||
}
|
}
|
||||||
|
|
||||||
const modelData = MODEL_MAP[providerName].find((m) => m.id === modelId);
|
const modelData = MODEL_MAP[providerName].find((m) => m.id === modelId);
|
||||||
|
|
||||||
if (!modelData || !modelData.cost_per_1m_tokens) {
|
if (!modelData || !modelData.cost_per_1m_tokens) {
|
||||||
log(
|
log(
|
||||||
'debug',
|
"debug",
|
||||||
`Cost data not found for model "${modelId}" under provider "${providerName}". Assuming zero cost.`
|
`Cost data not found for model "${modelId}" under provider "${providerName}". Assuming zero cost.`
|
||||||
);
|
);
|
||||||
return { inputCost: 0, outputCost: 0, currency: 'USD' }; // Default to zero cost
|
return { inputCost: 0, outputCost: 0, currency: "USD" }; // Default to zero cost
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure currency is part of the returned object, defaulting if not present
|
// Ensure currency is part of the returned object, defaulting if not present
|
||||||
const currency = modelData.cost_per_1m_tokens.currency || 'USD';
|
const currency = modelData.cost_per_1m_tokens.currency || "USD";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
inputCost: modelData.cost_per_1m_tokens.input || 0,
|
inputCost: modelData.cost_per_1m_tokens.input || 0,
|
||||||
outputCost: modelData.cost_per_1m_tokens.output || 0,
|
outputCost: modelData.cost_per_1m_tokens.output || 0,
|
||||||
currency: currency
|
currency: currency,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,13 +92,13 @@ const INITIAL_RETRY_DELAY_MS = 1000;
|
|||||||
|
|
||||||
// Helper function to check if an error is retryable
|
// Helper function to check if an error is retryable
|
||||||
function isRetryableError(error) {
|
function isRetryableError(error) {
|
||||||
const errorMessage = error.message?.toLowerCase() || '';
|
const errorMessage = error.message?.toLowerCase() || "";
|
||||||
return (
|
return (
|
||||||
errorMessage.includes('rate limit') ||
|
errorMessage.includes("rate limit") ||
|
||||||
errorMessage.includes('overloaded') ||
|
errorMessage.includes("overloaded") ||
|
||||||
errorMessage.includes('service temporarily unavailable') ||
|
errorMessage.includes("service temporarily unavailable") ||
|
||||||
errorMessage.includes('timeout') ||
|
errorMessage.includes("timeout") ||
|
||||||
errorMessage.includes('network error') ||
|
errorMessage.includes("network error") ||
|
||||||
error.status === 429 ||
|
error.status === 429 ||
|
||||||
error.status >= 500
|
error.status >= 500
|
||||||
);
|
);
|
||||||
@@ -123,7 +123,7 @@ function _extractErrorMessage(error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attempt 3: Look for nested error message in response body if it's JSON string
|
// Attempt 3: Look for nested error message in response body if it's JSON string
|
||||||
if (typeof error?.responseBody === 'string') {
|
if (typeof error?.responseBody === "string") {
|
||||||
try {
|
try {
|
||||||
const body = JSON.parse(error.responseBody);
|
const body = JSON.parse(error.responseBody);
|
||||||
if (body?.error?.message) {
|
if (body?.error?.message) {
|
||||||
@@ -135,20 +135,20 @@ function _extractErrorMessage(error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attempt 4: Use the top-level message if it exists
|
// Attempt 4: Use the top-level message if it exists
|
||||||
if (typeof error?.message === 'string' && error.message) {
|
if (typeof error?.message === "string" && error.message) {
|
||||||
return error.message;
|
return error.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt 5: Handle simple string errors
|
// Attempt 5: Handle simple string errors
|
||||||
if (typeof error === 'string') {
|
if (typeof error === "string") {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback
|
// Fallback
|
||||||
return 'An unknown AI service error occurred.';
|
return "An unknown AI service error occurred.";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Safety net
|
// Safety net
|
||||||
return 'Failed to extract error message.';
|
return "Failed to extract error message.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,17 +162,17 @@ function _extractErrorMessage(error) {
|
|||||||
*/
|
*/
|
||||||
function _resolveApiKey(providerName, session, projectRoot = null) {
|
function _resolveApiKey(providerName, session, projectRoot = null) {
|
||||||
const keyMap = {
|
const keyMap = {
|
||||||
openai: 'OPENAI_API_KEY',
|
openai: "OPENAI_API_KEY",
|
||||||
anthropic: 'ANTHROPIC_API_KEY',
|
anthropic: "ANTHROPIC_API_KEY",
|
||||||
google: 'GOOGLE_API_KEY',
|
google: "GOOGLE_API_KEY",
|
||||||
perplexity: 'PERPLEXITY_API_KEY',
|
perplexity: "PERPLEXITY_API_KEY",
|
||||||
mistral: 'MISTRAL_API_KEY',
|
mistral: "MISTRAL_API_KEY",
|
||||||
azure: 'AZURE_OPENAI_API_KEY',
|
azure: "AZURE_OPENAI_API_KEY",
|
||||||
openrouter: 'OPENROUTER_API_KEY',
|
openrouter: "OPENROUTER_API_KEY",
|
||||||
xai: 'XAI_API_KEY',
|
xai: "XAI_API_KEY",
|
||||||
ollama: 'OLLAMA_API_KEY',
|
ollama: "OLLAMA_API_KEY",
|
||||||
bedrock: 'AWS_ACCESS_KEY_ID',
|
bedrock: "AWS_ACCESS_KEY_ID",
|
||||||
vertex: 'GOOGLE_API_KEY'
|
vertex: "GOOGLE_API_KEY",
|
||||||
};
|
};
|
||||||
|
|
||||||
const envVarName = keyMap[providerName];
|
const envVarName = keyMap[providerName];
|
||||||
@@ -185,7 +185,7 @@ function _resolveApiKey(providerName, session, projectRoot = null) {
|
|||||||
const apiKey = resolveEnvVariable(envVarName, session, projectRoot);
|
const apiKey = resolveEnvVariable(envVarName, session, projectRoot);
|
||||||
|
|
||||||
// Special handling for providers that can use alternative auth
|
// Special handling for providers that can use alternative auth
|
||||||
if (providerName === 'ollama' || providerName === 'bedrock') {
|
if (providerName === "ollama" || providerName === "bedrock") {
|
||||||
return apiKey || null;
|
return apiKey || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ async function _attemptProviderCallWithRetries(
|
|||||||
try {
|
try {
|
||||||
if (getDebugFlag()) {
|
if (getDebugFlag()) {
|
||||||
log(
|
log(
|
||||||
'info',
|
"info",
|
||||||
`Attempt ${retries + 1}/${MAX_RETRIES + 1} calling ${fnName} (Provider: ${providerName}, Model: ${modelId}, Role: ${attemptRole})`
|
`Attempt ${retries + 1}/${MAX_RETRIES + 1} calling ${fnName} (Provider: ${providerName}, Model: ${modelId}, Role: ${attemptRole})`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -233,14 +233,14 @@ async function _attemptProviderCallWithRetries(
|
|||||||
|
|
||||||
if (getDebugFlag()) {
|
if (getDebugFlag()) {
|
||||||
log(
|
log(
|
||||||
'info',
|
"info",
|
||||||
`${fnName} succeeded for role ${attemptRole} (Provider: ${providerName}) on attempt ${retries + 1}`
|
`${fnName} succeeded for role ${attemptRole} (Provider: ${providerName}) on attempt ${retries + 1}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log(
|
log(
|
||||||
'warn',
|
"warn",
|
||||||
`Attempt ${retries + 1} failed for role ${attemptRole} (${fnName} / ${providerName}): ${error.message}`
|
`Attempt ${retries + 1} failed for role ${attemptRole} (${fnName} / ${providerName}): ${error.message}`
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -248,13 +248,13 @@ async function _attemptProviderCallWithRetries(
|
|||||||
retries++;
|
retries++;
|
||||||
const delay = INITIAL_RETRY_DELAY_MS * Math.pow(2, retries - 1);
|
const delay = INITIAL_RETRY_DELAY_MS * Math.pow(2, retries - 1);
|
||||||
log(
|
log(
|
||||||
'info',
|
"info",
|
||||||
`Something went wrong on the provider side. Retrying in ${delay / 1000}s...`
|
`Something went wrong on the provider side. Retrying in ${delay / 1000}s...`
|
||||||
);
|
);
|
||||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||||
} else {
|
} else {
|
||||||
log(
|
log(
|
||||||
'error',
|
"error",
|
||||||
`Something went wrong on the provider side. Max retries reached for role ${attemptRole} (${fnName} / ${providerName}).`
|
`Something went wrong on the provider side. Max retries reached for role ${attemptRole} (${fnName} / ${providerName}).`
|
||||||
);
|
);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -296,11 +296,11 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
...restApiParams
|
...restApiParams
|
||||||
} = params;
|
} = params;
|
||||||
if (getDebugFlag()) {
|
if (getDebugFlag()) {
|
||||||
log('info', `${serviceType}Service called`, {
|
log("info", `${serviceType}Service called`, {
|
||||||
role: initialRole,
|
role: initialRole,
|
||||||
commandName,
|
commandName,
|
||||||
outputType,
|
outputType,
|
||||||
projectRoot
|
projectRoot,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,23 +308,23 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
const userId = getUserId(effectiveProjectRoot);
|
const userId = getUserId(effectiveProjectRoot);
|
||||||
|
|
||||||
let sequence;
|
let sequence;
|
||||||
if (initialRole === 'main') {
|
if (initialRole === "main") {
|
||||||
sequence = ['main', 'fallback', 'research'];
|
sequence = ["main", "fallback", "research"];
|
||||||
} else if (initialRole === 'research') {
|
} else if (initialRole === "research") {
|
||||||
sequence = ['research', 'fallback', 'main'];
|
sequence = ["research", "fallback", "main"];
|
||||||
} else if (initialRole === 'fallback') {
|
} else if (initialRole === "fallback") {
|
||||||
sequence = ['fallback', 'main', 'research'];
|
sequence = ["fallback", "main", "research"];
|
||||||
} else {
|
} else {
|
||||||
log(
|
log(
|
||||||
'warn',
|
"warn",
|
||||||
`Unknown initial role: ${initialRole}. Defaulting to main -> fallback -> research sequence.`
|
`Unknown initial role: ${initialRole}. Defaulting to main -> fallback -> research sequence.`
|
||||||
);
|
);
|
||||||
sequence = ['main', 'fallback', 'research'];
|
sequence = ["main", "fallback", "research"];
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastError = null;
|
let lastError = null;
|
||||||
let lastCleanErrorMessage =
|
let lastCleanErrorMessage =
|
||||||
'AI service call failed for all configured roles.';
|
"AI service call failed for all configured roles.";
|
||||||
|
|
||||||
for (const currentRole of sequence) {
|
for (const currentRole of sequence) {
|
||||||
let providerName,
|
let providerName,
|
||||||
@@ -337,20 +337,20 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
telemetryData = null;
|
telemetryData = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log('info', `New AI service call with role: ${currentRole}`);
|
log("info", `New AI service call with role: ${currentRole}`);
|
||||||
|
|
||||||
if (currentRole === 'main') {
|
if (currentRole === "main") {
|
||||||
providerName = getMainProvider(effectiveProjectRoot);
|
providerName = getMainProvider(effectiveProjectRoot);
|
||||||
modelId = getMainModelId(effectiveProjectRoot);
|
modelId = getMainModelId(effectiveProjectRoot);
|
||||||
} else if (currentRole === 'research') {
|
} else if (currentRole === "research") {
|
||||||
providerName = getResearchProvider(effectiveProjectRoot);
|
providerName = getResearchProvider(effectiveProjectRoot);
|
||||||
modelId = getResearchModelId(effectiveProjectRoot);
|
modelId = getResearchModelId(effectiveProjectRoot);
|
||||||
} else if (currentRole === 'fallback') {
|
} else if (currentRole === "fallback") {
|
||||||
providerName = getFallbackProvider(effectiveProjectRoot);
|
providerName = getFallbackProvider(effectiveProjectRoot);
|
||||||
modelId = getFallbackModelId(effectiveProjectRoot);
|
modelId = getFallbackModelId(effectiveProjectRoot);
|
||||||
} else {
|
} else {
|
||||||
log(
|
log(
|
||||||
'error',
|
"error",
|
||||||
`Unknown role encountered in _unifiedServiceRunner: ${currentRole}`
|
`Unknown role encountered in _unifiedServiceRunner: ${currentRole}`
|
||||||
);
|
);
|
||||||
lastError =
|
lastError =
|
||||||
@@ -360,7 +360,7 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
|
|
||||||
if (!providerName || !modelId) {
|
if (!providerName || !modelId) {
|
||||||
log(
|
log(
|
||||||
'warn',
|
"warn",
|
||||||
`Skipping role '${currentRole}': Provider or Model ID not configured.`
|
`Skipping role '${currentRole}': Provider or Model ID not configured.`
|
||||||
);
|
);
|
||||||
lastError =
|
lastError =
|
||||||
@@ -375,7 +375,7 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
provider = PROVIDERS[providerName?.toLowerCase()];
|
provider = PROVIDERS[providerName?.toLowerCase()];
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
log(
|
log(
|
||||||
'warn',
|
"warn",
|
||||||
`Skipping role '${currentRole}': Provider '${providerName}' not supported.`
|
`Skipping role '${currentRole}': Provider '${providerName}' not supported.`
|
||||||
);
|
);
|
||||||
lastError =
|
lastError =
|
||||||
@@ -385,10 +385,10 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check API key if needed
|
// Check API key if needed
|
||||||
if (providerName?.toLowerCase() !== 'ollama') {
|
if (providerName?.toLowerCase() !== "ollama") {
|
||||||
if (!isApiKeySet(providerName, session, effectiveProjectRoot)) {
|
if (!isApiKeySet(providerName, session, effectiveProjectRoot)) {
|
||||||
log(
|
log(
|
||||||
'warn',
|
"warn",
|
||||||
`Skipping role '${currentRole}' (Provider: ${providerName}): API key not set or invalid.`
|
`Skipping role '${currentRole}' (Provider: ${providerName}): API key not set or invalid.`
|
||||||
);
|
);
|
||||||
lastError =
|
lastError =
|
||||||
@@ -404,17 +404,17 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
baseURL = getBaseUrlForRole(currentRole, effectiveProjectRoot);
|
baseURL = getBaseUrlForRole(currentRole, effectiveProjectRoot);
|
||||||
|
|
||||||
// For Azure, use the global Azure base URL if role-specific URL is not configured
|
// For Azure, use the global Azure base URL if role-specific URL is not configured
|
||||||
if (providerName?.toLowerCase() === 'azure' && !baseURL) {
|
if (providerName?.toLowerCase() === "azure" && !baseURL) {
|
||||||
baseURL = getAzureBaseURL(effectiveProjectRoot);
|
baseURL = getAzureBaseURL(effectiveProjectRoot);
|
||||||
log('debug', `Using global Azure base URL: ${baseURL}`);
|
log("debug", `Using global Azure base URL: ${baseURL}`);
|
||||||
} else if (providerName?.toLowerCase() === 'ollama' && !baseURL) {
|
} else if (providerName?.toLowerCase() === "ollama" && !baseURL) {
|
||||||
// For Ollama, use the global Ollama base URL if role-specific URL is not configured
|
// For Ollama, use the global Ollama base URL if role-specific URL is not configured
|
||||||
baseURL = getOllamaBaseURL(effectiveProjectRoot);
|
baseURL = getOllamaBaseURL(effectiveProjectRoot);
|
||||||
log('debug', `Using global Ollama base URL: ${baseURL}`);
|
log("debug", `Using global Ollama base URL: ${baseURL}`);
|
||||||
} else if (providerName?.toLowerCase() === 'bedrock' && !baseURL) {
|
} else if (providerName?.toLowerCase() === "bedrock" && !baseURL) {
|
||||||
// For Bedrock, use the global Bedrock base URL if role-specific URL is not configured
|
// For Bedrock, use the global Bedrock base URL if role-specific URL is not configured
|
||||||
baseURL = getBedrockBaseURL(effectiveProjectRoot);
|
baseURL = getBedrockBaseURL(effectiveProjectRoot);
|
||||||
log('debug', `Using global Bedrock base URL: ${baseURL}`);
|
log("debug", `Using global Bedrock base URL: ${baseURL}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get AI parameters for the current role
|
// Get AI parameters for the current role
|
||||||
@@ -429,12 +429,12 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
let providerSpecificParams = {};
|
let providerSpecificParams = {};
|
||||||
|
|
||||||
// Handle Vertex AI specific configuration
|
// Handle Vertex AI specific configuration
|
||||||
if (providerName?.toLowerCase() === 'vertex') {
|
if (providerName?.toLowerCase() === "vertex") {
|
||||||
// Get Vertex project ID and location
|
// Get Vertex project ID and location
|
||||||
const projectId =
|
const projectId =
|
||||||
getVertexProjectId(effectiveProjectRoot) ||
|
getVertexProjectId(effectiveProjectRoot) ||
|
||||||
resolveEnvVariable(
|
resolveEnvVariable(
|
||||||
'VERTEX_PROJECT_ID',
|
"VERTEX_PROJECT_ID",
|
||||||
session,
|
session,
|
||||||
effectiveProjectRoot
|
effectiveProjectRoot
|
||||||
);
|
);
|
||||||
@@ -442,15 +442,15 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
const location =
|
const location =
|
||||||
getVertexLocation(effectiveProjectRoot) ||
|
getVertexLocation(effectiveProjectRoot) ||
|
||||||
resolveEnvVariable(
|
resolveEnvVariable(
|
||||||
'VERTEX_LOCATION',
|
"VERTEX_LOCATION",
|
||||||
session,
|
session,
|
||||||
effectiveProjectRoot
|
effectiveProjectRoot
|
||||||
) ||
|
) ||
|
||||||
'us-central1';
|
"us-central1";
|
||||||
|
|
||||||
// Get credentials path if available
|
// Get credentials path if available
|
||||||
const credentialsPath = resolveEnvVariable(
|
const credentialsPath = resolveEnvVariable(
|
||||||
'GOOGLE_APPLICATION_CREDENTIALS',
|
"GOOGLE_APPLICATION_CREDENTIALS",
|
||||||
session,
|
session,
|
||||||
effectiveProjectRoot
|
effectiveProjectRoot
|
||||||
);
|
);
|
||||||
@@ -459,18 +459,18 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
providerSpecificParams = {
|
providerSpecificParams = {
|
||||||
projectId,
|
projectId,
|
||||||
location,
|
location,
|
||||||
...(credentialsPath && { credentials: { credentialsFromEnv: true } })
|
...(credentialsPath && { credentials: { credentialsFromEnv: true } }),
|
||||||
};
|
};
|
||||||
|
|
||||||
log(
|
log(
|
||||||
'debug',
|
"debug",
|
||||||
`Using Vertex AI configuration: Project ID=${projectId}, Location=${location}`
|
`Using Vertex AI configuration: Project ID=${projectId}, Location=${location}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = [];
|
const messages = [];
|
||||||
if (systemPrompt) {
|
if (systemPrompt) {
|
||||||
messages.push({ role: 'system', content: systemPrompt });
|
messages.push({ role: "system", content: systemPrompt });
|
||||||
}
|
}
|
||||||
|
|
||||||
// IN THE FUTURE WHEN DOING CONTEXT IMPROVEMENTS
|
// IN THE FUTURE WHEN DOING CONTEXT IMPROVEMENTS
|
||||||
@@ -492,9 +492,9 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
if (prompt) {
|
if (prompt) {
|
||||||
messages.push({ role: 'user', content: prompt });
|
messages.push({ role: "user", content: prompt });
|
||||||
} else {
|
} else {
|
||||||
throw new Error('User prompt content is missing.');
|
throw new Error("User prompt content is missing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const callParams = {
|
const callParams = {
|
||||||
@@ -504,9 +504,9 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
temperature: roleParams.temperature,
|
temperature: roleParams.temperature,
|
||||||
messages,
|
messages,
|
||||||
...(baseURL && { baseURL }),
|
...(baseURL && { baseURL }),
|
||||||
...(serviceType === 'generateObject' && { schema, objectName }),
|
...(serviceType === "generateObject" && { schema, objectName }),
|
||||||
...providerSpecificParams,
|
...providerSpecificParams,
|
||||||
...restApiParams
|
...restApiParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
providerResponse = await _attemptProviderCallWithRetries(
|
providerResponse = await _attemptProviderCallWithRetries(
|
||||||
@@ -527,7 +527,7 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
modelId,
|
modelId,
|
||||||
inputTokens: providerResponse.usage.inputTokens,
|
inputTokens: providerResponse.usage.inputTokens,
|
||||||
outputTokens: providerResponse.usage.outputTokens,
|
outputTokens: providerResponse.usage.outputTokens,
|
||||||
outputType
|
outputType,
|
||||||
});
|
});
|
||||||
} catch (telemetryError) {
|
} catch (telemetryError) {
|
||||||
// logAiUsage already logs its own errors and returns null on failure
|
// logAiUsage already logs its own errors and returns null on failure
|
||||||
@@ -535,21 +535,21 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
}
|
}
|
||||||
} else if (userId && providerResponse && !providerResponse.usage) {
|
} else if (userId && providerResponse && !providerResponse.usage) {
|
||||||
log(
|
log(
|
||||||
'warn',
|
"warn",
|
||||||
`Cannot log telemetry for ${commandName} (${providerName}/${modelId}): AI result missing 'usage' data. (May be expected for streams)`
|
`Cannot log telemetry for ${commandName} (${providerName}/${modelId}): AI result missing 'usage' data. (May be expected for streams)`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let finalMainResult;
|
let finalMainResult;
|
||||||
if (serviceType === 'generateText') {
|
if (serviceType === "generateText") {
|
||||||
finalMainResult = providerResponse.text;
|
finalMainResult = providerResponse.text;
|
||||||
} else if (serviceType === 'generateObject') {
|
} else if (serviceType === "generateObject") {
|
||||||
finalMainResult = providerResponse.object;
|
finalMainResult = providerResponse.object;
|
||||||
} else if (serviceType === 'streamText') {
|
} else if (serviceType === "streamText") {
|
||||||
finalMainResult = providerResponse;
|
finalMainResult = providerResponse;
|
||||||
} else {
|
} else {
|
||||||
log(
|
log(
|
||||||
'error',
|
"error",
|
||||||
`Unknown serviceType in _unifiedServiceRunner: ${serviceType}`
|
`Unknown serviceType in _unifiedServiceRunner: ${serviceType}`
|
||||||
);
|
);
|
||||||
finalMainResult = providerResponse;
|
finalMainResult = providerResponse;
|
||||||
@@ -557,37 +557,38 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
mainResult: finalMainResult,
|
mainResult: finalMainResult,
|
||||||
telemetryData: telemetryData
|
telemetryData: telemetryData,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const cleanMessage = _extractErrorMessage(error);
|
const cleanMessage = _extractErrorMessage(error);
|
||||||
log(
|
log(
|
||||||
'error',
|
"error",
|
||||||
`Service call failed for role ${currentRole} (Provider: ${providerName || 'unknown'}, Model: ${modelId || 'unknown'}): ${cleanMessage}`
|
`Service call failed for role ${currentRole} (Provider: ${providerName || "unknown"}, Model: ${modelId || "unknown"}): ${cleanMessage}`
|
||||||
);
|
);
|
||||||
lastError = error;
|
lastError = error;
|
||||||
lastCleanErrorMessage = cleanMessage;
|
lastCleanErrorMessage = cleanMessage;
|
||||||
|
|
||||||
if (serviceType === 'generateObject') {
|
if (serviceType === "generateObject") {
|
||||||
const lowerCaseMessage = cleanMessage.toLowerCase();
|
const lowerCaseMessage = cleanMessage.toLowerCase();
|
||||||
if (
|
if (
|
||||||
lowerCaseMessage.includes(
|
lowerCaseMessage.includes(
|
||||||
'no endpoints found that support tool use'
|
"no endpoints found that support tool use"
|
||||||
) ||
|
) ||
|
||||||
lowerCaseMessage.includes('does not support tool_use') ||
|
lowerCaseMessage.includes("does not support tool_use") ||
|
||||||
lowerCaseMessage.includes('tool use is not supported') ||
|
lowerCaseMessage.includes("tool use is not supported") ||
|
||||||
lowerCaseMessage.includes('tools are not supported') ||
|
lowerCaseMessage.includes("tools are not supported") ||
|
||||||
lowerCaseMessage.includes('function calling is not supported')
|
lowerCaseMessage.includes("function calling is not supported") ||
|
||||||
|
lowerCaseMessage.includes("tool use is not supported")
|
||||||
) {
|
) {
|
||||||
const specificErrorMsg = `Model '${modelId || 'unknown'}' via provider '${providerName || 'unknown'}' does not support the 'tool use' required by generateObjectService. Please configure a model that supports tool/function calling for the '${currentRole}' role, or use generateTextService if structured output is not strictly required.`;
|
const specificErrorMsg = `Model '${modelId || "unknown"}' via provider '${providerName || "unknown"}' does not support the 'tool use' required by generateObjectService. Please configure a model that supports tool/function calling for the '${currentRole}' role, or use generateTextService if structured output is not strictly required.`;
|
||||||
log('error', `[Tool Support Error] ${specificErrorMsg}`);
|
log("error", `[Tool Support Error] ${specificErrorMsg}`);
|
||||||
throw new Error(specificErrorMsg);
|
throw new Error(specificErrorMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log('error', `All roles in the sequence [${sequence.join(', ')}] failed.`);
|
log("error", `All roles in the sequence [${sequence.join(", ")}] failed.`);
|
||||||
throw new Error(lastCleanErrorMessage);
|
throw new Error(lastCleanErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,10 +608,10 @@ async function _unifiedServiceRunner(serviceType, params) {
|
|||||||
*/
|
*/
|
||||||
async function generateTextService(params) {
|
async function generateTextService(params) {
|
||||||
// Ensure default outputType if not provided
|
// Ensure default outputType if not provided
|
||||||
const defaults = { outputType: 'cli' };
|
const defaults = { outputType: "cli" };
|
||||||
const combinedParams = { ...defaults, ...params };
|
const combinedParams = { ...defaults, ...params };
|
||||||
// TODO: Validate commandName exists?
|
// TODO: Validate commandName exists?
|
||||||
return _unifiedServiceRunner('generateText', combinedParams);
|
return _unifiedServiceRunner("generateText", combinedParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -628,13 +629,13 @@ async function generateTextService(params) {
|
|||||||
* @returns {Promise<object>} Result object containing the stream and usage data.
|
* @returns {Promise<object>} Result object containing the stream and usage data.
|
||||||
*/
|
*/
|
||||||
async function streamTextService(params) {
|
async function streamTextService(params) {
|
||||||
const defaults = { outputType: 'cli' };
|
const defaults = { outputType: "cli" };
|
||||||
const combinedParams = { ...defaults, ...params };
|
const combinedParams = { ...defaults, ...params };
|
||||||
// TODO: Validate commandName exists?
|
// TODO: Validate commandName exists?
|
||||||
// NOTE: Telemetry for streaming might be tricky as usage data often comes at the end.
|
// NOTE: Telemetry for streaming might be tricky as usage data often comes at the end.
|
||||||
// The current implementation logs *after* the stream is returned.
|
// The current implementation logs *after* the stream is returned.
|
||||||
// We might need to adjust how usage is captured/logged for streams.
|
// We might need to adjust how usage is captured/logged for streams.
|
||||||
return _unifiedServiceRunner('streamText', combinedParams);
|
return _unifiedServiceRunner("streamText", combinedParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -656,13 +657,13 @@ async function streamTextService(params) {
|
|||||||
*/
|
*/
|
||||||
async function generateObjectService(params) {
|
async function generateObjectService(params) {
|
||||||
const defaults = {
|
const defaults = {
|
||||||
objectName: 'generated_object',
|
objectName: "generated_object",
|
||||||
maxRetries: 3,
|
maxRetries: 3,
|
||||||
outputType: 'cli'
|
outputType: "cli",
|
||||||
};
|
};
|
||||||
const combinedParams = { ...defaults, ...params };
|
const combinedParams = { ...defaults, ...params };
|
||||||
// TODO: Validate commandName exists?
|
// TODO: Validate commandName exists?
|
||||||
return _unifiedServiceRunner('generateObject', combinedParams);
|
return _unifiedServiceRunner("generateObject", combinedParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Telemetry Function ---
|
// --- Telemetry Function ---
|
||||||
@@ -684,10 +685,10 @@ async function logAiUsage({
|
|||||||
modelId,
|
modelId,
|
||||||
inputTokens,
|
inputTokens,
|
||||||
outputTokens,
|
outputTokens,
|
||||||
outputType
|
outputType,
|
||||||
}) {
|
}) {
|
||||||
try {
|
try {
|
||||||
const isMCP = outputType === 'mcp';
|
const isMCP = outputType === "mcp";
|
||||||
const timestamp = new Date().toISOString();
|
const timestamp = new Date().toISOString();
|
||||||
const totalTokens = (inputTokens || 0) + (outputTokens || 0);
|
const totalTokens = (inputTokens || 0) + (outputTokens || 0);
|
||||||
|
|
||||||
@@ -711,19 +712,19 @@ async function logAiUsage({
|
|||||||
outputTokens: outputTokens || 0,
|
outputTokens: outputTokens || 0,
|
||||||
totalTokens,
|
totalTokens,
|
||||||
totalCost: parseFloat(totalCost.toFixed(6)),
|
totalCost: parseFloat(totalCost.toFixed(6)),
|
||||||
currency // Add currency to the telemetry data
|
currency, // Add currency to the telemetry data
|
||||||
};
|
};
|
||||||
|
|
||||||
if (getDebugFlag()) {
|
if (getDebugFlag()) {
|
||||||
log('info', 'AI Usage Telemetry:', telemetryData);
|
log("info", "AI Usage Telemetry:", telemetryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO (Subtask 77.2): Send telemetryData securely to the external endpoint.
|
// TODO (Subtask 77.2): Send telemetryData securely to the external endpoint.
|
||||||
|
|
||||||
return telemetryData;
|
return telemetryData;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log('error', `Failed to log AI usage telemetry: ${error.message}`, {
|
log("error", `Failed to log AI usage telemetry: ${error.message}`, {
|
||||||
error
|
error,
|
||||||
});
|
});
|
||||||
// Don't re-throw; telemetry failure shouldn't block core functionality.
|
// Don't re-throw; telemetry failure shouldn't block core functionality.
|
||||||
return null;
|
return null;
|
||||||
@@ -734,5 +735,5 @@ export {
|
|||||||
generateTextService,
|
generateTextService,
|
||||||
streamTextService,
|
streamTextService,
|
||||||
generateObjectService,
|
generateObjectService,
|
||||||
logAiUsage
|
logAiUsage,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -153,7 +153,7 @@
|
|||||||
"id": "sonar-pro",
|
"id": "sonar-pro",
|
||||||
"swe_score": 0,
|
"swe_score": 0,
|
||||||
"cost_per_1m_tokens": { "input": 3, "output": 15 },
|
"cost_per_1m_tokens": { "input": 3, "output": 15 },
|
||||||
"allowed_roles": ["research"],
|
"allowed_roles": ["main", "research"],
|
||||||
"max_tokens": 8700
|
"max_tokens": 8700
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -174,14 +174,14 @@
|
|||||||
"id": "sonar-reasoning-pro",
|
"id": "sonar-reasoning-pro",
|
||||||
"swe_score": 0.211,
|
"swe_score": 0.211,
|
||||||
"cost_per_1m_tokens": { "input": 2, "output": 8 },
|
"cost_per_1m_tokens": { "input": 2, "output": 8 },
|
||||||
"allowed_roles": ["main", "fallback"],
|
"allowed_roles": ["main", "research", "fallback"],
|
||||||
"max_tokens": 8700
|
"max_tokens": 8700
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "sonar-reasoning",
|
"id": "sonar-reasoning",
|
||||||
"swe_score": 0.211,
|
"swe_score": 0.211,
|
||||||
"cost_per_1m_tokens": { "input": 1, "output": 5 },
|
"cost_per_1m_tokens": { "input": 1, "output": 5 },
|
||||||
"allowed_roles": ["main", "fallback"],
|
"allowed_roles": ["main", "research", "fallback"],
|
||||||
"max_tokens": 8700
|
"max_tokens": 8700
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { generateText, streamText, generateObject } from 'ai';
|
import { generateText, streamText, generateObject } from "ai";
|
||||||
import { log } from '../../scripts/modules/index.js';
|
import { log } from "../../scripts/modules/index.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all AI providers
|
* Base class for all AI providers
|
||||||
@@ -7,7 +7,7 @@ import { log } from '../../scripts/modules/index.js';
|
|||||||
export class BaseAIProvider {
|
export class BaseAIProvider {
|
||||||
constructor() {
|
constructor() {
|
||||||
if (this.constructor === BaseAIProvider) {
|
if (this.constructor === BaseAIProvider) {
|
||||||
throw new Error('BaseAIProvider cannot be instantiated directly');
|
throw new Error("BaseAIProvider cannot be instantiated directly");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each provider must set their name
|
// Each provider must set their name
|
||||||
@@ -51,10 +51,10 @@ export class BaseAIProvider {
|
|||||||
params.temperature !== undefined &&
|
params.temperature !== undefined &&
|
||||||
(params.temperature < 0 || params.temperature > 1)
|
(params.temperature < 0 || params.temperature > 1)
|
||||||
) {
|
) {
|
||||||
throw new Error('Temperature must be between 0 and 1');
|
throw new Error("Temperature must be between 0 and 1");
|
||||||
}
|
}
|
||||||
if (params.maxTokens !== undefined && params.maxTokens <= 0) {
|
if (params.maxTokens !== undefined && params.maxTokens <= 0) {
|
||||||
throw new Error('maxTokens must be greater than 0');
|
throw new Error("maxTokens must be greater than 0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,13 +63,13 @@ export class BaseAIProvider {
|
|||||||
*/
|
*/
|
||||||
validateMessages(messages) {
|
validateMessages(messages) {
|
||||||
if (!messages || !Array.isArray(messages) || messages.length === 0) {
|
if (!messages || !Array.isArray(messages) || messages.length === 0) {
|
||||||
throw new Error('Invalid or empty messages array provided');
|
throw new Error("Invalid or empty messages array provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const msg of messages) {
|
for (const msg of messages) {
|
||||||
if (!msg.role || !msg.content) {
|
if (!msg.role || !msg.content) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Invalid message format. Each message must have role and content'
|
"Invalid message format. Each message must have role and content"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,9 +79,9 @@ export class BaseAIProvider {
|
|||||||
* Common error handler
|
* Common error handler
|
||||||
*/
|
*/
|
||||||
handleError(operation, error) {
|
handleError(operation, error) {
|
||||||
const errorMessage = error.message || 'Unknown error occurred';
|
const errorMessage = error.message || "Unknown error occurred";
|
||||||
log('error', `${this.name} ${operation} failed: ${errorMessage}`, {
|
log("error", `${this.name} ${operation} failed: ${errorMessage}`, {
|
||||||
error
|
error,
|
||||||
});
|
});
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`${this.name} API error during ${operation}: ${errorMessage}`
|
`${this.name} API error during ${operation}: ${errorMessage}`
|
||||||
@@ -93,7 +93,7 @@ export class BaseAIProvider {
|
|||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
getClient(params) {
|
getClient(params) {
|
||||||
throw new Error('getClient must be implemented by provider');
|
throw new Error("getClient must be implemented by provider");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,7 +105,7 @@ export class BaseAIProvider {
|
|||||||
this.validateMessages(params.messages);
|
this.validateMessages(params.messages);
|
||||||
|
|
||||||
log(
|
log(
|
||||||
'debug',
|
"debug",
|
||||||
`Generating ${this.name} text with model: ${params.modelId}`
|
`Generating ${this.name} text with model: ${params.modelId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -114,11 +114,11 @@ export class BaseAIProvider {
|
|||||||
model: client(params.modelId),
|
model: client(params.modelId),
|
||||||
messages: params.messages,
|
messages: params.messages,
|
||||||
maxTokens: params.maxTokens,
|
maxTokens: params.maxTokens,
|
||||||
temperature: params.temperature
|
temperature: params.temperature,
|
||||||
});
|
});
|
||||||
|
|
||||||
log(
|
log(
|
||||||
'debug',
|
"debug",
|
||||||
`${this.name} generateText completed successfully for model: ${params.modelId}`
|
`${this.name} generateText completed successfully for model: ${params.modelId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -127,11 +127,11 @@ export class BaseAIProvider {
|
|||||||
usage: {
|
usage: {
|
||||||
inputTokens: result.usage?.promptTokens,
|
inputTokens: result.usage?.promptTokens,
|
||||||
outputTokens: result.usage?.completionTokens,
|
outputTokens: result.usage?.completionTokens,
|
||||||
totalTokens: result.usage?.totalTokens
|
totalTokens: result.usage?.totalTokens,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.handleError('text generation', error);
|
this.handleError("text generation", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,24 +143,24 @@ export class BaseAIProvider {
|
|||||||
this.validateParams(params);
|
this.validateParams(params);
|
||||||
this.validateMessages(params.messages);
|
this.validateMessages(params.messages);
|
||||||
|
|
||||||
log('debug', `Streaming ${this.name} text with model: ${params.modelId}`);
|
log("debug", `Streaming ${this.name} text with model: ${params.modelId}`);
|
||||||
|
|
||||||
const client = this.getClient(params);
|
const client = this.getClient(params);
|
||||||
const stream = await streamText({
|
const stream = await streamText({
|
||||||
model: client(params.modelId),
|
model: client(params.modelId),
|
||||||
messages: params.messages,
|
messages: params.messages,
|
||||||
maxTokens: params.maxTokens,
|
maxTokens: params.maxTokens,
|
||||||
temperature: params.temperature
|
temperature: params.temperature,
|
||||||
});
|
});
|
||||||
|
|
||||||
log(
|
log(
|
||||||
'debug',
|
"debug",
|
||||||
`${this.name} streamText initiated successfully for model: ${params.modelId}`
|
`${this.name} streamText initiated successfully for model: ${params.modelId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.handleError('text streaming', error);
|
this.handleError("text streaming", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,14 +173,14 @@ export class BaseAIProvider {
|
|||||||
this.validateMessages(params.messages);
|
this.validateMessages(params.messages);
|
||||||
|
|
||||||
if (!params.schema) {
|
if (!params.schema) {
|
||||||
throw new Error('Schema is required for object generation');
|
throw new Error("Schema is required for object generation");
|
||||||
}
|
}
|
||||||
if (!params.objectName) {
|
if (!params.objectName) {
|
||||||
throw new Error('Object name is required for object generation');
|
throw new Error("Object name is required for object generation");
|
||||||
}
|
}
|
||||||
|
|
||||||
log(
|
log(
|
||||||
'debug',
|
"debug",
|
||||||
`Generating ${this.name} object ('${params.objectName}') with model: ${params.modelId}`
|
`Generating ${this.name} object ('${params.objectName}') with model: ${params.modelId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -189,13 +189,13 @@ export class BaseAIProvider {
|
|||||||
model: client(params.modelId),
|
model: client(params.modelId),
|
||||||
messages: params.messages,
|
messages: params.messages,
|
||||||
schema: params.schema,
|
schema: params.schema,
|
||||||
mode: 'tool',
|
mode: "auto",
|
||||||
maxTokens: params.maxTokens,
|
maxTokens: params.maxTokens,
|
||||||
temperature: params.temperature
|
temperature: params.temperature,
|
||||||
});
|
});
|
||||||
|
|
||||||
log(
|
log(
|
||||||
'debug',
|
"debug",
|
||||||
`${this.name} generateObject completed successfully for model: ${params.modelId}`
|
`${this.name} generateObject completed successfully for model: ${params.modelId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -204,11 +204,11 @@ export class BaseAIProvider {
|
|||||||
usage: {
|
usage: {
|
||||||
inputTokens: result.usage?.promptTokens,
|
inputTokens: result.usage?.promptTokens,
|
||||||
outputTokens: result.usage?.completionTokens,
|
outputTokens: result.usage?.completionTokens,
|
||||||
totalTokens: result.usage?.totalTokens
|
totalTokens: result.usage?.totalTokens,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.handleError('object generation', error);
|
this.handleError("object generation", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user