Compare commits
2 Commits
task-maste
...
chore/fix.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
565090257c | ||
|
|
2c27aeda20 |
@@ -1,8 +0,0 @@
|
|||||||
---
|
|
||||||
"task-master-ai": patch
|
|
||||||
---
|
|
||||||
|
|
||||||
Fix scope-up/down prompts to include all required fields for better AI model compatibility
|
|
||||||
|
|
||||||
- Added missing `priority` field to scope adjustment prompts to prevent validation errors with Claude-code and other models
|
|
||||||
- Ensures generated JSON includes all fields required by the schema
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
---
|
|
||||||
"task-master-ai": patch
|
|
||||||
---
|
|
||||||
|
|
||||||
Fix MCP scope-up/down tools not finding tasks
|
|
||||||
|
|
||||||
- Fixed task ID parsing in MCP layer - now correctly converts string IDs to numbers
|
|
||||||
- scope_up_task and scope_down_task MCP tools now work properly
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
"task-master-ai": patch
|
|
||||||
---
|
|
||||||
|
|
||||||
Improve AI provider compatibility for JSON generation
|
|
||||||
|
|
||||||
- Fixed schema compatibility issues between Perplexity and OpenAI o3 models
|
|
||||||
- Removed nullable/default modifiers from Zod schemas for broader compatibility
|
|
||||||
- Added automatic JSON repair for malformed AI responses (handles cases like missing array values)
|
|
||||||
- Perplexity now uses JSON mode for more reliable structured output
|
|
||||||
- Post-processing handles default values separately from schema validation
|
|
||||||
46
.github/scripts/tag-extension.mjs
vendored
46
.github/scripts/tag-extension.mjs
vendored
@@ -62,51 +62,13 @@ assert(rootPkg.repository, 'root package.json must have a repository field');
|
|||||||
const tag = `${pkg.name}@${pkg.version}`;
|
const tag = `${pkg.name}@${pkg.version}`;
|
||||||
|
|
||||||
// Get repository URL from root package.json
|
// Get repository URL from root package.json
|
||||||
// Get repository URL and clean it up for git ls-remote
|
const repoUrl = rootPkg.repository.url;
|
||||||
let repoUrl = rootPkg.repository.url || rootPkg.repository;
|
|
||||||
if (typeof repoUrl === 'string') {
|
|
||||||
// Convert git+https://github.com/... to https://github.com/...
|
|
||||||
repoUrl = repoUrl.replace(/^git\+/, '');
|
|
||||||
// Ensure it ends with .git for proper remote access
|
|
||||||
if (!repoUrl.endsWith('.git')) {
|
|
||||||
repoUrl += '.git';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Checking remote repository: ${repoUrl} for tag: ${tag}`);
|
const { status, stdout, error } = spawnSync('git', ['ls-remote', repoUrl, tag]);
|
||||||
|
|
||||||
let gitResult = spawnSync('git', ['ls-remote', repoUrl, tag], {
|
assert.equal(status, 0, error);
|
||||||
encoding: 'utf8',
|
|
||||||
env: { ...process.env }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (gitResult.status !== 0) {
|
const exists = String(stdout).trim() !== '';
|
||||||
console.error('Git ls-remote failed:');
|
|
||||||
console.error('Exit code:', gitResult.status);
|
|
||||||
console.error('Error:', gitResult.error);
|
|
||||||
console.error('Stderr:', gitResult.stderr);
|
|
||||||
console.error('Command:', `git ls-remote ${repoUrl} ${tag}`);
|
|
||||||
|
|
||||||
// For CI environments, try using origin instead of the full URL
|
|
||||||
if (process.env.CI) {
|
|
||||||
console.log('Retrying with origin remote...');
|
|
||||||
gitResult = spawnSync('git', ['ls-remote', 'origin', tag], {
|
|
||||||
encoding: 'utf8'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (gitResult.status !== 0) {
|
|
||||||
throw new Error(
|
|
||||||
`Failed to check remote for tag ${tag}. Exit code: ${gitResult.status}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
`Failed to check remote for tag ${tag}. Exit code: ${gitResult.status}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const exists = String(gitResult.stdout).trim() !== '';
|
|
||||||
|
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
console.log(`Creating new extension tag: ${tag}`);
|
console.log(`Creating new extension tag: ${tag}`);
|
||||||
|
|||||||
5
.github/workflows/pre-release.yml
vendored
5
.github/workflows/pre-release.yml
vendored
@@ -3,12 +3,11 @@ name: Pre-Release (RC)
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch: # Allows manual triggering from GitHub UI/API
|
workflow_dispatch: # Allows manual triggering from GitHub UI/API
|
||||||
|
|
||||||
concurrency: pre-release-${{ github.ref_name }}
|
concurrency: pre-release-${{ github.ref }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
rc:
|
rc:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Only allow pre-releases on non-main branches
|
|
||||||
if: github.ref != 'refs/heads/main'
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
34
.github/workflows/release.yml
vendored
34
.github/workflows/release.yml
vendored
@@ -38,26 +38,26 @@ jobs:
|
|||||||
run: npm ci
|
run: npm ci
|
||||||
timeout-minutes: 2
|
timeout-minutes: 2
|
||||||
|
|
||||||
- name: Check pre-release mode
|
- name: Exit pre-release mode and clean up
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
echo "🔄 Ensuring we're not in pre-release mode for main branch..."
|
||||||
echo "🔍 Checking pre-release mode status..."
|
|
||||||
if [[ -f .changeset/pre.json ]]; then
|
# Exit pre-release mode if we're in it
|
||||||
echo "❌ ERROR: Main branch is in pre-release mode!"
|
npx changeset pre exit || echo "Not in pre-release mode"
|
||||||
echo ""
|
|
||||||
echo "Pre-release mode should only be used on feature branches, not main."
|
# Remove pre.json file if it exists (belt and suspenders approach)
|
||||||
echo ""
|
if [ -f .changeset/pre.json ]; then
|
||||||
echo "To fix this, run the following commands locally:"
|
echo "🧹 Removing pre.json file..."
|
||||||
echo " npx changeset pre exit"
|
rm -f .changeset/pre.json
|
||||||
echo " git add -u"
|
|
||||||
echo " git commit -m 'chore: exit pre-release mode'"
|
|
||||||
echo " git push origin main"
|
|
||||||
echo ""
|
|
||||||
echo "Then re-run this workflow."
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✅ Not in pre-release mode - proceeding with release"
|
# Verify the file is gone
|
||||||
|
if [ ! -f .changeset/pre.json ]; then
|
||||||
|
echo "✅ pre.json successfully removed"
|
||||||
|
else
|
||||||
|
echo "❌ Failed to remove pre.json"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Create Release Pull Request or Publish to npm
|
- name: Create Release Pull Request or Publish to npm
|
||||||
uses: changesets/action@v1
|
uses: changesets/action@v1
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ export async function scopeDownDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse task IDs - convert to numbers as expected by scopeDownTask
|
// Parse task IDs
|
||||||
const taskIds = id.split(',').map((taskId) => parseInt(taskId.trim(), 10));
|
const taskIds = id.split(',').map((taskId) => taskId.trim());
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Scoping down tasks: ${taskIds.join(', ')}, strength: ${strength}, research: ${research}`
|
`Scoping down tasks: ${taskIds.join(', ')}, strength: ${strength}, research: ${research}`
|
||||||
@@ -90,10 +90,10 @@ export async function scopeDownDirect(args, log, context = {}) {
|
|||||||
projectRoot,
|
projectRoot,
|
||||||
commandName: 'scope-down',
|
commandName: 'scope-down',
|
||||||
outputType: 'mcp',
|
outputType: 'mcp',
|
||||||
tag,
|
tag
|
||||||
research
|
|
||||||
},
|
},
|
||||||
'json' // outputFormat
|
'json', // outputFormat
|
||||||
|
research
|
||||||
);
|
);
|
||||||
|
|
||||||
// Restore normal logging
|
// Restore normal logging
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ export async function scopeUpDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse task IDs - convert to numbers as expected by scopeUpTask
|
// Parse task IDs
|
||||||
const taskIds = id.split(',').map((taskId) => parseInt(taskId.trim(), 10));
|
const taskIds = id.split(',').map((taskId) => taskId.trim());
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Scoping up tasks: ${taskIds.join(', ')}, strength: ${strength}, research: ${research}`
|
`Scoping up tasks: ${taskIds.join(', ')}, strength: ${strength}, research: ${research}`
|
||||||
@@ -90,10 +90,10 @@ export async function scopeUpDirect(args, log, context = {}) {
|
|||||||
projectRoot,
|
projectRoot,
|
||||||
commandName: 'scope-up',
|
commandName: 'scope-up',
|
||||||
outputType: 'mcp',
|
outputType: 'mcp',
|
||||||
tag,
|
tag
|
||||||
research
|
|
||||||
},
|
},
|
||||||
'json' // outputFormat
|
'json', // outputFormat
|
||||||
|
research
|
||||||
);
|
);
|
||||||
|
|
||||||
// Restore normal logging
|
// Restore normal logging
|
||||||
|
|||||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "task-master-ai",
|
"name": "task-master-ai",
|
||||||
"version": "0.23.0",
|
"version": "0.22.1-rc.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "task-master-ai",
|
"name": "task-master-ai",
|
||||||
"version": "0.23.0",
|
"version": "0.22.1-rc.0",
|
||||||
"license": "MIT WITH Commons-Clause",
|
"license": "MIT WITH Commons-Clause",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
@@ -46,7 +46,6 @@
|
|||||||
"helmet": "^8.1.0",
|
"helmet": "^8.1.0",
|
||||||
"inquirer": "^12.5.0",
|
"inquirer": "^12.5.0",
|
||||||
"jsonc-parser": "^3.3.1",
|
"jsonc-parser": "^3.3.1",
|
||||||
"jsonrepair": "^3.13.0",
|
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lru-cache": "^10.2.0",
|
"lru-cache": "^10.2.0",
|
||||||
"ollama-ai-provider": "^1.2.0",
|
"ollama-ai-provider": "^1.2.0",
|
||||||
@@ -85,7 +84,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apps/extension": {
|
"apps/extension": {
|
||||||
"version": "0.23.0",
|
"version": "0.22.3",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/modifiers": "^9.0.0",
|
"@dnd-kit/modifiers": "^9.0.0",
|
||||||
@@ -14943,15 +14942,6 @@
|
|||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsonrepair": {
|
|
||||||
"version": "3.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.13.0.tgz",
|
|
||||||
"integrity": "sha512-5YRzlAQ7tuzV1nAJu3LvDlrKtBFIALHN2+a+I1MGJCt3ldRDBF/bZuvIPzae8Epot6KBXd0awRZZcuoeAsZ/mw==",
|
|
||||||
"license": "ISC",
|
|
||||||
"bin": {
|
|
||||||
"jsonrepair": "bin/cli.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jsonwebtoken": {
|
"node_modules/jsonwebtoken": {
|
||||||
"version": "9.0.2",
|
"version": "9.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||||
|
|||||||
@@ -73,7 +73,6 @@
|
|||||||
"helmet": "^8.1.0",
|
"helmet": "^8.1.0",
|
||||||
"inquirer": "^12.5.0",
|
"inquirer": "^12.5.0",
|
||||||
"jsonc-parser": "^3.3.1",
|
"jsonc-parser": "^3.3.1",
|
||||||
"jsonrepair": "^3.13.0",
|
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lru-cache": "^10.2.0",
|
"lru-cache": "^10.2.0",
|
||||||
"ollama-ai-provider": "^1.2.0",
|
"ollama-ai-provider": "^1.2.0",
|
||||||
|
|||||||
@@ -1479,8 +1479,7 @@ function registerCommands(programInstance) {
|
|||||||
projectRoot: taskMaster.getProjectRoot(),
|
projectRoot: taskMaster.getProjectRoot(),
|
||||||
tag,
|
tag,
|
||||||
commandName: 'scope-up',
|
commandName: 'scope-up',
|
||||||
outputType: 'cli',
|
outputType: 'cli'
|
||||||
research: options.research || false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await scopeUpTask(
|
const result = await scopeUpTask(
|
||||||
@@ -1606,8 +1605,7 @@ function registerCommands(programInstance) {
|
|||||||
projectRoot: taskMaster.getProjectRoot(),
|
projectRoot: taskMaster.getProjectRoot(),
|
||||||
tag,
|
tag,
|
||||||
commandName: 'scope-down',
|
commandName: 'scope-down',
|
||||||
outputType: 'cli',
|
outputType: 'cli'
|
||||||
research: options.research || false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await scopeDownTask(
|
const result = await scopeDownTask(
|
||||||
|
|||||||
@@ -23,14 +23,14 @@ import { displayAiUsageSummary } from '../ui.js';
|
|||||||
|
|
||||||
// Define the Zod schema for a SINGLE task object
|
// Define the Zod schema for a SINGLE task object
|
||||||
const prdSingleTaskSchema = z.object({
|
const prdSingleTaskSchema = z.object({
|
||||||
id: z.number(),
|
id: z.number().int().positive(),
|
||||||
title: z.string().min(1),
|
title: z.string().min(1),
|
||||||
description: z.string().min(1),
|
description: z.string().min(1),
|
||||||
details: z.string(),
|
details: z.string().nullable(),
|
||||||
testStrategy: z.string(),
|
testStrategy: z.string().nullable(),
|
||||||
priority: z.enum(['high', 'medium', 'low']),
|
priority: z.enum(['high', 'medium', 'low']).nullable(),
|
||||||
dependencies: z.array(z.number()),
|
dependencies: z.array(z.number().int().positive()).nullable(),
|
||||||
status: z.string()
|
status: z.string().nullable()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Define the Zod schema for the ENTIRE expected AI response object
|
// Define the Zod schema for the ENTIRE expected AI response object
|
||||||
@@ -257,15 +257,10 @@ async function parsePRD(prdPath, tasksPath, numTasks, options = {}) {
|
|||||||
return {
|
return {
|
||||||
...task,
|
...task,
|
||||||
id: newId,
|
id: newId,
|
||||||
status: task.status || 'pending',
|
status: 'pending',
|
||||||
priority: task.priority || 'medium',
|
priority: task.priority || 'medium',
|
||||||
dependencies: Array.isArray(task.dependencies) ? task.dependencies : [],
|
dependencies: Array.isArray(task.dependencies) ? task.dependencies : [],
|
||||||
subtasks: [],
|
subtasks: []
|
||||||
// Ensure all required fields have values (even if empty strings)
|
|
||||||
title: task.title || '',
|
|
||||||
description: task.description || '',
|
|
||||||
details: task.details || '',
|
|
||||||
testStrategy: task.testStrategy || ''
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ ${
|
|||||||
}
|
}
|
||||||
|
|
||||||
Return a JSON object with a "subtasks" array. Each subtask should have:
|
Return a JSON object with a "subtasks" array. Each subtask should have:
|
||||||
- id: Sequential NUMBER starting from 1 (e.g., 1, 2, 3 - NOT "1", "2", "3")
|
- id: Sequential number starting from 1
|
||||||
- title: Clear, specific title
|
- title: Clear, specific title
|
||||||
- description: Detailed description
|
- description: Detailed description
|
||||||
- dependencies: Array of dependency IDs as STRINGS (use format ["${task.id}.1", "${task.id}.2"] for siblings, or empty array [] for no dependencies)
|
- dependencies: Array of dependency IDs as STRINGS (use format ["${task.id}.1", "${task.id}.2"] for siblings, or empty array [] for no dependencies)
|
||||||
@@ -345,9 +345,7 @@ Return a JSON object with a "subtasks" array. Each subtask should have:
|
|||||||
- status: "pending"
|
- status: "pending"
|
||||||
- testStrategy: Testing approach
|
- testStrategy: Testing approach
|
||||||
|
|
||||||
IMPORTANT:
|
IMPORTANT: Dependencies must be strings, not numbers!
|
||||||
- The 'id' field must be a NUMBER, not a string!
|
|
||||||
- Dependencies must be strings, not numbers!
|
|
||||||
|
|
||||||
Ensure the JSON is valid and properly formatted.`;
|
Ensure the JSON is valid and properly formatted.`;
|
||||||
|
|
||||||
@@ -360,14 +358,14 @@ Ensure the JSON is valid and properly formatted.`;
|
|||||||
description: z.string().min(10),
|
description: z.string().min(10),
|
||||||
dependencies: z.array(z.string()),
|
dependencies: z.array(z.string()),
|
||||||
details: z.string().min(20),
|
details: z.string().min(20),
|
||||||
status: z.string(),
|
status: z.string().default('pending'),
|
||||||
testStrategy: z.string()
|
testStrategy: z.string().nullable().default('')
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
const aiResult = await generateObjectService({
|
const aiResult = await generateObjectService({
|
||||||
role: context.research ? 'research' : 'main',
|
role: 'main',
|
||||||
session: context.session,
|
session: context.session,
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
prompt,
|
prompt,
|
||||||
@@ -379,21 +377,14 @@ Ensure the JSON is valid and properly formatted.`;
|
|||||||
|
|
||||||
const generatedSubtasks = aiResult.mainResult.subtasks || [];
|
const generatedSubtasks = aiResult.mainResult.subtasks || [];
|
||||||
|
|
||||||
// Post-process generated subtasks to ensure defaults
|
|
||||||
const processedGeneratedSubtasks = generatedSubtasks.map((subtask) => ({
|
|
||||||
...subtask,
|
|
||||||
status: subtask.status || 'pending',
|
|
||||||
testStrategy: subtask.testStrategy || ''
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Update task with preserved subtasks + newly generated ones
|
// Update task with preserved subtasks + newly generated ones
|
||||||
task.subtasks = [...preservedSubtasks, ...processedGeneratedSubtasks];
|
task.subtasks = [...preservedSubtasks, ...generatedSubtasks];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
updatedTask: task,
|
updatedTask: task,
|
||||||
regenerated: true,
|
regenerated: true,
|
||||||
preserved: preservedSubtasks.length,
|
preserved: preservedSubtasks.length,
|
||||||
generated: processedGeneratedSubtasks.length
|
generated: generatedSubtasks.length
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log(
|
log(
|
||||||
@@ -466,7 +457,6 @@ ADJUSTMENT REQUIREMENTS:
|
|||||||
- description: Updated task description
|
- description: Updated task description
|
||||||
- details: Updated implementation details
|
- details: Updated implementation details
|
||||||
- testStrategy: Updated test strategy
|
- testStrategy: Updated test strategy
|
||||||
- priority: Task priority ('low', 'medium', or 'high')
|
|
||||||
|
|
||||||
Ensure the JSON is valid and properly formatted.`;
|
Ensure the JSON is valid and properly formatted.`;
|
||||||
|
|
||||||
@@ -511,11 +501,14 @@ async function adjustTaskComplexity(
|
|||||||
.string()
|
.string()
|
||||||
.min(1)
|
.min(1)
|
||||||
.describe('Updated testing approach for the adjusted scope'),
|
.describe('Updated testing approach for the adjusted scope'),
|
||||||
priority: z.enum(['low', 'medium', 'high']).describe('Task priority level')
|
priority: z
|
||||||
|
.enum(['low', 'medium', 'high'])
|
||||||
|
.optional()
|
||||||
|
.describe('Task priority level')
|
||||||
});
|
});
|
||||||
|
|
||||||
const aiResult = await generateObjectService({
|
const aiResult = await generateObjectService({
|
||||||
role: context.research ? 'research' : 'main',
|
role: 'main',
|
||||||
session: context.session,
|
session: context.session,
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
prompt,
|
prompt,
|
||||||
@@ -527,16 +520,10 @@ async function adjustTaskComplexity(
|
|||||||
|
|
||||||
const updatedTaskData = aiResult.mainResult;
|
const updatedTaskData = aiResult.mainResult;
|
||||||
|
|
||||||
// Ensure priority has a value (in case AI didn't provide one)
|
|
||||||
const processedTaskData = {
|
|
||||||
...updatedTaskData,
|
|
||||||
priority: updatedTaskData.priority || task.priority || 'medium'
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
updatedTask: {
|
updatedTask: {
|
||||||
...task,
|
...task,
|
||||||
...processedTaskData
|
...updatedTaskData
|
||||||
},
|
},
|
||||||
telemetryData: aiResult.telemetryData
|
telemetryData: aiResult.telemetryData
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
import {
|
import { generateObject, generateText, streamText } from 'ai';
|
||||||
generateObject,
|
|
||||||
generateText,
|
|
||||||
streamText,
|
|
||||||
zodSchema,
|
|
||||||
JSONParseError,
|
|
||||||
NoObjectGeneratedError
|
|
||||||
} from 'ai';
|
|
||||||
import { jsonrepair } from 'jsonrepair';
|
|
||||||
import { log } from '../../scripts/modules/utils.js';
|
import { log } from '../../scripts/modules/utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -214,8 +206,8 @@ export class BaseAIProvider {
|
|||||||
const result = await generateObject({
|
const result = await generateObject({
|
||||||
model: client(params.modelId),
|
model: client(params.modelId),
|
||||||
messages: params.messages,
|
messages: params.messages,
|
||||||
schema: zodSchema(params.schema),
|
schema: params.schema,
|
||||||
mode: params.mode || 'auto',
|
mode: 'auto',
|
||||||
maxTokens: params.maxTokens,
|
maxTokens: params.maxTokens,
|
||||||
temperature: params.temperature
|
temperature: params.temperature
|
||||||
});
|
});
|
||||||
@@ -234,43 +226,6 @@ export class BaseAIProvider {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Check if this is a JSON parsing error that we can potentially fix
|
|
||||||
if (
|
|
||||||
NoObjectGeneratedError.isInstance(error) &&
|
|
||||||
JSONParseError.isInstance(error.cause) &&
|
|
||||||
error.cause.text
|
|
||||||
) {
|
|
||||||
log(
|
|
||||||
'warn',
|
|
||||||
`${this.name} generated malformed JSON, attempting to repair...`
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Use jsonrepair to fix the malformed JSON
|
|
||||||
const repairedJson = jsonrepair(error.cause.text);
|
|
||||||
const parsed = JSON.parse(repairedJson);
|
|
||||||
|
|
||||||
log('info', `Successfully repaired ${this.name} JSON output`);
|
|
||||||
|
|
||||||
// Return in the expected format
|
|
||||||
return {
|
|
||||||
object: parsed,
|
|
||||||
usage: {
|
|
||||||
// Extract usage information from the error if available
|
|
||||||
inputTokens: error.usage?.promptTokens || 0,
|
|
||||||
outputTokens: error.usage?.completionTokens || 0,
|
|
||||||
totalTokens: error.usage?.totalTokens || 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (repairError) {
|
|
||||||
log(
|
|
||||||
'error',
|
|
||||||
`Failed to repair ${this.name} JSON: ${repairError.message}`
|
|
||||||
);
|
|
||||||
// Fall through to handleError with original error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleError('object generation', error);
|
this.handleError('object generation', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,21 +44,4 @@ export class PerplexityAIProvider extends BaseAIProvider {
|
|||||||
this.handleError('client initialization', error);
|
this.handleError('client initialization', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Override generateObject to use JSON mode for Perplexity
|
|
||||||
*
|
|
||||||
* NOTE: Perplexity models (especially sonar models) have known issues
|
|
||||||
* generating valid JSON, particularly with array fields. They often
|
|
||||||
* generate malformed JSON like "dependencies": , instead of "dependencies": []
|
|
||||||
*
|
|
||||||
* The base provider now handles JSON repair automatically for all providers.
|
|
||||||
*/
|
|
||||||
async generateObject(params) {
|
|
||||||
// Force JSON mode for Perplexity as it may help with reliability
|
|
||||||
return super.generateObject({
|
|
||||||
...params,
|
|
||||||
mode: 'json'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user