mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-30 21:49:13 +00:00
Compare commits
1 Commits
chore/rele
...
sdk-typesc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17e335833d |
132
.github/workflows/release-sdk.yml
vendored
132
.github/workflows/release-sdk.yml
vendored
@@ -34,8 +34,7 @@ on:
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
# Serialize all release workflows (CLI + SDK) to avoid racing on `main` pushes.
|
||||
group: 'release-main'
|
||||
group: '${{ github.workflow }}'
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
@@ -51,6 +50,7 @@ jobs:
|
||||
packages: 'write'
|
||||
id-token: 'write'
|
||||
issues: 'write'
|
||||
pull-requests: 'write'
|
||||
outputs:
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
|
||||
@@ -128,13 +128,12 @@ jobs:
|
||||
IS_PREVIEW: '${{ steps.vars.outputs.is_preview }}'
|
||||
MANUAL_VERSION: '${{ inputs.version }}'
|
||||
|
||||
- name: 'Set SDK package version'
|
||||
- name: 'Set SDK package version (local only)'
|
||||
env:
|
||||
RELEASE_VERSION: '${{ steps.version.outputs.RELEASE_VERSION }}'
|
||||
run: |-
|
||||
# Ensure the package version matches the computed release version.
|
||||
# This is required for nightly/preview because npm does not allow re-publishing the same version.
|
||||
# Using --no-git-tag-version because we create tags via GitHub Release, not npm.
|
||||
npm version -w @qwen-code/sdk "${RELEASE_VERSION}" --no-git-tag-version --allow-same-version
|
||||
|
||||
- name: 'Build CLI Bundle'
|
||||
@@ -169,40 +168,37 @@ jobs:
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: 'Create and switch to a release branch (stable only)'
|
||||
- name: 'Build SDK'
|
||||
working-directory: 'packages/sdk-typescript'
|
||||
run: |-
|
||||
npm run build
|
||||
|
||||
- name: 'Publish @qwen-code/sdk'
|
||||
working-directory: 'packages/sdk-typescript'
|
||||
run: |-
|
||||
npm publish --access public --tag=${{ steps.version.outputs.NPM_TAG }} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ secrets.NPM_TOKEN }}'
|
||||
|
||||
- name: 'Create and switch to a release branch'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' && steps.vars.outputs.is_nightly == 'false' && steps.vars.outputs.is_preview == 'false' }}
|
||||
id: 'release_branch'
|
||||
env:
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
run: |-
|
||||
set -euo pipefail
|
||||
BRANCH_NAME="release/sdk-typescript/${RELEASE_TAG}"
|
||||
|
||||
# Make reruns idempotent: reuse an existing remote branch if it already exists.
|
||||
if git show-ref --verify --quiet "refs/heads/${BRANCH_NAME}"; then
|
||||
git switch "${BRANCH_NAME}"
|
||||
elif git ls-remote --exit-code --heads origin "${BRANCH_NAME}" >/dev/null 2>&1; then
|
||||
git fetch origin "${BRANCH_NAME}:${BRANCH_NAME}"
|
||||
git switch "${BRANCH_NAME}"
|
||||
else
|
||||
git switch -c "${BRANCH_NAME}"
|
||||
fi
|
||||
|
||||
git switch -c "${BRANCH_NAME}"
|
||||
echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: 'Build SDK'
|
||||
working-directory: 'packages/sdk-typescript'
|
||||
run: |-
|
||||
npm run build
|
||||
|
||||
- name: 'Commit and Push package version to release branch (stable only)'
|
||||
- name: 'Commit and Push package version (stable only)'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' && steps.vars.outputs.is_nightly == 'false' && steps.vars.outputs.is_preview == 'false' }}
|
||||
env:
|
||||
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
run: |-
|
||||
# Only persist version bumps after a successful publish.
|
||||
git add packages/sdk-typescript/package.json package-lock.json
|
||||
if git diff --staged --quiet; then
|
||||
echo "No version changes to commit"
|
||||
@@ -212,47 +208,9 @@ jobs:
|
||||
echo "Pushing release branch to remote..."
|
||||
git push --set-upstream origin "${BRANCH_NAME}" --follow-tags
|
||||
|
||||
- name: 'Check if @qwen-code/sdk version is already published (rerun safety)'
|
||||
id: 'npm_check'
|
||||
env:
|
||||
RELEASE_VERSION: '${{ steps.version.outputs.RELEASE_VERSION }}'
|
||||
run: |-
|
||||
set -euo pipefail
|
||||
if npm view "@qwen-code/sdk@${RELEASE_VERSION}" version >/dev/null 2>&1; then
|
||||
echo "already_published=true" >> "${GITHUB_OUTPUT}"
|
||||
echo "@qwen-code/sdk@${RELEASE_VERSION} already exists on npm."
|
||||
else
|
||||
echo "already_published=false" >> "${GITHUB_OUTPUT}"
|
||||
fi
|
||||
|
||||
- name: 'Publish @qwen-code/sdk'
|
||||
working-directory: 'packages/sdk-typescript'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'true' || steps.npm_check.outputs.already_published != 'true' }}
|
||||
run: |-
|
||||
npm publish --access public --tag=${{ steps.version.outputs.NPM_TAG }} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ secrets.NPM_TOKEN }}'
|
||||
|
||||
- name: 'Check if GitHub Release already exists (rerun safety)'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' }}
|
||||
id: 'gh_release_check'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
run: |-
|
||||
set -euo pipefail
|
||||
if gh release view "sdk-typescript-${RELEASE_TAG}" >/dev/null 2>&1; then
|
||||
echo "already_exists=true" >> "${GITHUB_OUTPUT}"
|
||||
echo "GitHub Release sdk-typescript-${RELEASE_TAG} already exists."
|
||||
else
|
||||
echo "already_exists=false" >> "${GITHUB_OUTPUT}"
|
||||
fi
|
||||
|
||||
- name: 'Create GitHub Release and Tag'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' && steps.gh_release_check.outputs.already_exists != 'true' }}
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' }}
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_BRANCH: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
@@ -278,26 +236,48 @@ jobs:
|
||||
--generate-notes \
|
||||
${PRERELEASE_FLAG}
|
||||
|
||||
- name: 'Merge release changes back to main'
|
||||
- name: 'Create PR to merge release branch into main'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' && steps.vars.outputs.is_nightly == 'false' && steps.vars.outputs.is_preview == 'false' }}
|
||||
id: 'pr'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_BRANCH: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
run: |-
|
||||
set -euo pipefail
|
||||
|
||||
pr_url="$(gh pr list --head "${RELEASE_BRANCH}" --base main --json url --jq '.[0].url')"
|
||||
if [[ -z "${pr_url}" ]]; then
|
||||
pr_url="$(gh pr create \
|
||||
--base main \
|
||||
--head "${RELEASE_BRANCH}" \
|
||||
--title "chore(release): sdk-typescript ${RELEASE_TAG}" \
|
||||
--body "Automated release PR for sdk-typescript ${RELEASE_TAG}.")"
|
||||
fi
|
||||
|
||||
echo "PR_URL=${pr_url}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: 'Wait for CI checks to complete'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' && steps.vars.outputs.is_nightly == 'false' && steps.vars.outputs.is_preview == 'false' }}
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
RELEASE_BRANCH: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
PR_URL: '${{ steps.pr.outputs.PR_URL }}'
|
||||
run: |-
|
||||
# Checkout main branch
|
||||
git fetch origin main
|
||||
git checkout main
|
||||
git pull origin main
|
||||
set -euo pipefail
|
||||
echo "Waiting for CI checks to complete..."
|
||||
gh pr checks "${PR_URL}" --watch --interval 30
|
||||
|
||||
# Only fast-forward merge back to main.
|
||||
# Purpose: avoid creating silent merge commits in an automated workflow (no PR review),
|
||||
# and fail loudly when history has diverged so a human can resolve it.
|
||||
git merge "${RELEASE_BRANCH}" --ff-only
|
||||
|
||||
# Push changes
|
||||
git push origin main
|
||||
- name: 'Enable auto-merge for release PR'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' && steps.vars.outputs.is_nightly == 'false' && steps.vars.outputs.is_preview == 'false' }}
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
PR_URL: '${{ steps.pr.outputs.PR_URL }}'
|
||||
run: |-
|
||||
set -euo pipefail
|
||||
gh pr merge "${PR_URL}" --merge --auto
|
||||
|
||||
- name: 'Create Issue on Failure'
|
||||
if: |-
|
||||
|
||||
74
.github/workflows/release.yml
vendored
74
.github/workflows/release.yml
vendored
@@ -38,11 +38,6 @@ on:
|
||||
type: 'boolean'
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
# Serialize all release workflows (CLI + SDK) to avoid racing on `main` pushes.
|
||||
group: 'release-main'
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: 'ubuntu-latest'
|
||||
@@ -155,19 +150,8 @@ jobs:
|
||||
env:
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
run: |-
|
||||
set -euo pipefail
|
||||
BRANCH_NAME="release/${RELEASE_TAG}"
|
||||
|
||||
# Make reruns idempotent: reuse an existing remote branch if it already exists.
|
||||
if git show-ref --verify --quiet "refs/heads/${BRANCH_NAME}"; then
|
||||
git switch "${BRANCH_NAME}"
|
||||
elif git ls-remote --exit-code --heads origin "${BRANCH_NAME}" >/dev/null 2>&1; then
|
||||
git fetch origin "${BRANCH_NAME}:${BRANCH_NAME}"
|
||||
git switch "${BRANCH_NAME}"
|
||||
else
|
||||
git switch -c "${BRANCH_NAME}"
|
||||
fi
|
||||
|
||||
git switch -c "${BRANCH_NAME}"
|
||||
echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: 'Update package versions'
|
||||
@@ -207,47 +191,16 @@ jobs:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
scope: '@qwen-code'
|
||||
|
||||
- name: 'Check if @qwen-code/qwen-code version is already published (rerun safety)'
|
||||
id: 'npm_check'
|
||||
env:
|
||||
RELEASE_VERSION: '${{ steps.version.outputs.RELEASE_VERSION }}'
|
||||
run: |-
|
||||
set -euo pipefail
|
||||
if npm view "@qwen-code/qwen-code@${RELEASE_VERSION}" version >/dev/null 2>&1; then
|
||||
echo "already_published=true" >> "${GITHUB_OUTPUT}"
|
||||
echo "@qwen-code/qwen-code@${RELEASE_VERSION} already exists on npm."
|
||||
else
|
||||
echo "already_published=false" >> "${GITHUB_OUTPUT}"
|
||||
fi
|
||||
|
||||
- name: 'Publish @qwen-code/qwen-code'
|
||||
working-directory: 'dist'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'true' || steps.npm_check.outputs.already_published != 'true' }}
|
||||
run: |-
|
||||
npm publish --access public --tag=${{ steps.version.outputs.NPM_TAG }} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ secrets.NPM_TOKEN }}'
|
||||
|
||||
- name: 'Check if GitHub Release already exists (rerun safety)'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' }}
|
||||
id: 'gh_release_check'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
run: |-
|
||||
set -euo pipefail
|
||||
if gh release view "${RELEASE_TAG}" >/dev/null 2>&1; then
|
||||
echo "already_exists=true" >> "${GITHUB_OUTPUT}"
|
||||
echo "GitHub Release ${RELEASE_TAG} already exists."
|
||||
else
|
||||
echo "already_exists=false" >> "${GITHUB_OUTPUT}"
|
||||
fi
|
||||
|
||||
- name: 'Create GitHub Release and Tag'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' && steps.gh_release_check.outputs.already_exists != 'true' }}
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' }}
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_BRANCH: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
@@ -261,33 +214,12 @@ jobs:
|
||||
--notes-start-tag "$PREVIOUS_RELEASE_TAG" \
|
||||
--generate-notes
|
||||
|
||||
- name: 'Merge release changes back to main'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' && steps.vars.outputs.is_nightly == 'false' && steps.vars.outputs.is_preview == 'false' }}
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
RELEASE_BRANCH: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
run: |-
|
||||
# Checkout main branch
|
||||
git fetch origin main
|
||||
git checkout main
|
||||
git pull origin main
|
||||
|
||||
# Only fast-forward merge back to main.
|
||||
# Purpose: avoid creating silent merge commits in an automated workflow (no PR review),
|
||||
# and fail loudly when history has diverged so a human can resolve it.
|
||||
git merge "${RELEASE_BRANCH}" --ff-only
|
||||
|
||||
# Push changes
|
||||
git push origin main
|
||||
|
||||
- name: 'Create Issue on Failure'
|
||||
if: |-
|
||||
${{ failure() }}
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_TAG: "${{ steps.version.outputs.RELEASE_TAG || 'N/A' }}"
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }} || "N/A"'
|
||||
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
run: |-
|
||||
gh issue create \
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -18593,7 +18593,7 @@
|
||||
},
|
||||
"packages/sdk-typescript": {
|
||||
"name": "@qwen-code/sdk",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.25.1",
|
||||
|
||||
@@ -169,44 +169,6 @@ describe('ShellTool', () => {
|
||||
});
|
||||
expect(invocation.getDescription()).not.toContain('[background]');
|
||||
});
|
||||
|
||||
describe('is_background parameter coercion', () => {
|
||||
it('should accept string "true" as boolean true', () => {
|
||||
const invocation = shellTool.build({
|
||||
command: 'npm run dev',
|
||||
is_background: 'true' as unknown as boolean,
|
||||
});
|
||||
expect(invocation).toBeDefined();
|
||||
expect(invocation.getDescription()).toContain('[background]');
|
||||
});
|
||||
|
||||
it('should accept string "false" as boolean false', () => {
|
||||
const invocation = shellTool.build({
|
||||
command: 'npm run build',
|
||||
is_background: 'false' as unknown as boolean,
|
||||
});
|
||||
expect(invocation).toBeDefined();
|
||||
expect(invocation.getDescription()).not.toContain('[background]');
|
||||
});
|
||||
|
||||
it('should accept string "True" as boolean true', () => {
|
||||
const invocation = shellTool.build({
|
||||
command: 'npm run dev',
|
||||
is_background: 'True' as unknown as boolean,
|
||||
});
|
||||
expect(invocation).toBeDefined();
|
||||
expect(invocation.getDescription()).toContain('[background]');
|
||||
});
|
||||
|
||||
it('should accept string "False" as boolean false', () => {
|
||||
const invocation = shellTool.build({
|
||||
command: 'npm run build',
|
||||
is_background: 'False' as unknown as boolean,
|
||||
});
|
||||
expect(invocation).toBeDefined();
|
||||
expect(invocation.getDescription()).not.toContain('[background]');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
|
||||
@@ -122,91 +122,4 @@ describe('SchemaValidator', () => {
|
||||
};
|
||||
expect(SchemaValidator.validate(schema, params)).not.toBeNull();
|
||||
});
|
||||
|
||||
describe('boolean string coercion', () => {
|
||||
const booleanSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
is_background: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
required: ['is_background'],
|
||||
};
|
||||
|
||||
it('should coerce string "true" to boolean true', () => {
|
||||
const params = { is_background: 'true' };
|
||||
expect(SchemaValidator.validate(booleanSchema, params)).toBeNull();
|
||||
expect(params.is_background).toBe(true);
|
||||
});
|
||||
|
||||
it('should coerce string "True" to boolean true', () => {
|
||||
const params = { is_background: 'True' };
|
||||
expect(SchemaValidator.validate(booleanSchema, params)).toBeNull();
|
||||
expect(params.is_background).toBe(true);
|
||||
});
|
||||
|
||||
it('should coerce string "TRUE" to boolean true', () => {
|
||||
const params = { is_background: 'TRUE' };
|
||||
expect(SchemaValidator.validate(booleanSchema, params)).toBeNull();
|
||||
expect(params.is_background).toBe(true);
|
||||
});
|
||||
|
||||
it('should coerce string "false" to boolean false', () => {
|
||||
const params = { is_background: 'false' };
|
||||
expect(SchemaValidator.validate(booleanSchema, params)).toBeNull();
|
||||
expect(params.is_background).toBe(false);
|
||||
});
|
||||
|
||||
it('should coerce string "False" to boolean false', () => {
|
||||
const params = { is_background: 'False' };
|
||||
expect(SchemaValidator.validate(booleanSchema, params)).toBeNull();
|
||||
expect(params.is_background).toBe(false);
|
||||
});
|
||||
|
||||
it('should coerce string "FALSE" to boolean false', () => {
|
||||
const params = { is_background: 'FALSE' };
|
||||
expect(SchemaValidator.validate(booleanSchema, params)).toBeNull();
|
||||
expect(params.is_background).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle nested objects with string booleans', () => {
|
||||
const nestedSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
options: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean' },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const params = { options: { enabled: 'true' } };
|
||||
expect(SchemaValidator.validate(nestedSchema, params)).toBeNull();
|
||||
expect((params.options as unknown as { enabled: boolean }).enabled).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('should not affect non-boolean strings', () => {
|
||||
const mixedSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
is_active: { type: 'boolean' },
|
||||
},
|
||||
};
|
||||
const params = { name: 'trueman', is_active: 'true' };
|
||||
expect(SchemaValidator.validate(mixedSchema, params)).toBeNull();
|
||||
expect(params.name).toBe('trueman');
|
||||
expect(params.is_active).toBe(true);
|
||||
});
|
||||
|
||||
it('should pass through actual boolean values unchanged', () => {
|
||||
const params = { is_background: true };
|
||||
expect(SchemaValidator.validate(booleanSchema, params)).toBeNull();
|
||||
expect(params.is_background).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,12 +41,14 @@ export class SchemaValidator {
|
||||
return 'Value of params must be an object';
|
||||
}
|
||||
const validate = ajValidator.compile(schema);
|
||||
let valid = validate(data);
|
||||
const valid = validate(data);
|
||||
if (!valid && validate.errors) {
|
||||
// Coerce string boolean values ("true"/"false") to actual booleans
|
||||
fixBooleanValues(data as Record<string, unknown>);
|
||||
// Find any True or False values and lowercase them
|
||||
fixBooleanCasing(data as Record<string, unknown>);
|
||||
|
||||
const validate = ajValidator.compile(schema);
|
||||
const valid = validate(data);
|
||||
|
||||
valid = validate(data);
|
||||
if (!valid && validate.errors) {
|
||||
return ajValidator.errorsText(validate.errors, { dataVar: 'params' });
|
||||
}
|
||||
@@ -55,29 +57,13 @@ export class SchemaValidator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerces string boolean values to actual booleans.
|
||||
* This handles cases where LLMs return "true"/"false" strings instead of boolean values,
|
||||
* which is common with self-hosted LLMs.
|
||||
*
|
||||
* Converts:
|
||||
* - "true", "True", "TRUE" -> true
|
||||
* - "false", "False", "FALSE" -> false
|
||||
*/
|
||||
function fixBooleanValues(data: Record<string, unknown>) {
|
||||
function fixBooleanCasing(data: Record<string, unknown>) {
|
||||
for (const key of Object.keys(data)) {
|
||||
if (!(key in data)) continue;
|
||||
const value = data[key];
|
||||
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
fixBooleanValues(value as Record<string, unknown>);
|
||||
} else if (typeof value === 'string') {
|
||||
const lower = value.toLowerCase();
|
||||
if (lower === 'true') {
|
||||
data[key] = true;
|
||||
} else if (lower === 'false') {
|
||||
data[key] = false;
|
||||
}
|
||||
}
|
||||
if (typeof data[key] === 'object') {
|
||||
fixBooleanCasing(data[key] as Record<string, unknown>);
|
||||
} else if (data[key] === 'True') data[key] = 'true';
|
||||
else if (data[key] === 'False') data[key] = 'false';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user