mirror of
https://github.com/anomalyco/opencode.git
synced 2026-10-02 13:36:22 +00:00
Compare commits
84
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38290a67a1 | ||
|
|
05018b8862 | ||
|
|
1404566141 | ||
|
|
37a426e7f4 | ||
|
|
ef867267a5 | ||
|
|
e366ab40c4 | ||
|
|
317dfbc6b3 | ||
|
|
a8761745a2 | ||
|
|
b47d8db9bc | ||
|
|
4c0d0ff478 | ||
|
|
ba0eeddff4 | ||
|
|
3bae692dd2 | ||
|
|
56145fce05 | ||
|
|
4f0d6d0ab6 | ||
|
|
5fdeed271d | ||
|
|
ab5d49666a | ||
|
|
8a3185b66c | ||
|
|
37f3e239c6 | ||
|
|
2deb0e250f | ||
|
|
e41756cfc4 | ||
|
|
8b24fc8c2a | ||
|
|
0f9f0a9ae3 | ||
|
|
f9e61fd9a3 | ||
|
|
5256112bac | ||
|
|
afc5d1e21f | ||
|
|
80ecb3fc89 | ||
|
|
31a683f5ca | ||
|
|
5d707f1a2b | ||
|
|
e2948f5a51 | ||
|
|
fbe8a9f7b4 | ||
|
|
033b9ea560 | ||
|
|
7bb3fbf095 | ||
|
|
507e117466 | ||
|
|
1549712761 | ||
|
|
4f37be6265 | ||
|
|
7b04288099 | ||
|
|
96339544bf | ||
|
|
b03d760f43 | ||
|
|
b73e75fb6a | ||
|
|
08c15ec1cd | ||
|
|
48f3c2e923 | ||
|
|
3d94a90978 | ||
|
|
2c33922412 | ||
|
|
7e42a897bc | ||
|
|
d8663e1fe8 | ||
|
|
a6b10e3e87 | ||
|
|
3400375fb3 | ||
|
|
ffb51318f6 | ||
|
|
b1157ebb04 | ||
|
|
3e4cdcd180 | ||
|
|
0a6f380db7 | ||
|
|
44336e8b84 | ||
|
|
20cf09752d | ||
|
|
966e718b31 | ||
|
|
cf0c9fb914 | ||
|
|
bebc3640f4 | ||
|
|
aa6a4f93bd | ||
|
|
8433dd732f | ||
|
|
c34d09fe97 | ||
|
|
2fb7985bb7 | ||
|
|
a931e8a9fd | ||
|
|
149acd7141 | ||
|
|
adf4b023d8 | ||
|
|
f406d93a66 | ||
|
|
9faf99a4ab | ||
|
|
19f203837e | ||
|
|
f0f4c94c0a | ||
|
|
9c4c5870b6 | ||
|
|
924c12b291 | ||
|
|
155bc7df75 | ||
|
|
d280eaf961 | ||
|
|
5556ad563d | ||
|
|
629e7aaa31 | ||
|
|
dd2af0e5fc | ||
|
|
a923596ee7 | ||
|
|
91b9bc52a4 | ||
|
|
f46fa72a92 | ||
|
|
14636b9900 | ||
|
|
b020f1018b | ||
|
|
7878744505 | ||
|
|
599bdc982e | ||
|
|
82bb5ff88a | ||
|
|
0159356be6 | ||
|
|
9388b94a97 |
No files matched your search
@@ -45,14 +45,16 @@ runs:
|
||||
- name: Get cache directory
|
||||
id: cache
|
||||
shell: bash
|
||||
run: echo "dir=$(bun pm cache)" >> "$GITHUB_OUTPUT"
|
||||
run: |
|
||||
echo "dir=$(bun pm cache)" >> "$GITHUB_OUTPUT"
|
||||
echo "version=$(bun --version)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore Bun dependencies
|
||||
id: bun-cache
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: ${{ steps.cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ steps.cache.outputs.version }}-${{ hashFiles('bun.lock', 'patches/**') }}
|
||||
|
||||
- name: Install setuptools for distutils compatibility
|
||||
run: python3 -m pip install setuptools || pip install setuptools || true
|
||||
@@ -75,4 +77,4 @@ runs:
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: ${{ steps.cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
|
||||
key: ${{ steps.bun-cache.outputs.cache-primary-key }}
|
||||
@@ -7,6 +7,10 @@ on:
|
||||
branches: [dev, v2]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ case(github.ref == 'refs/heads/dev', format('{0}-{1}', github.workflow, github.run_id), format('{0}-{1}', github.workflow, github.event.pull_request.number || github.ref)) }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: typecheck
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
close-non-compliant:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Close non-compliant issues and PRs after 2 hours
|
||||
- name: Close non-compliant issues and PRs after 72 hours
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
|
||||
with:
|
||||
script: |
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const twoHours = 2 * 60 * 60 * 1000;
|
||||
const seventyTwoHours = 72 * 60 * 60 * 1000;
|
||||
const orgMemberAssociations = new Set(['OWNER', 'MEMBER']);
|
||||
const agentLogin = 'opencode-agent[bot]';
|
||||
const { data: file } = await github.rest.repos.getContent({
|
||||
@@ -87,14 +87,14 @@ jobs:
|
||||
if (!complianceComment) continue;
|
||||
|
||||
const commentAge = now - new Date(complianceComment.created_at).getTime();
|
||||
if (commentAge < twoHours) {
|
||||
core.info(`${kind} #${item.number} still within 2-hour window (${Math.round(commentAge / 60000)}m elapsed)`);
|
||||
if (commentAge < seventyTwoHours) {
|
||||
core.info(`${kind} #${item.number} still within 72-hour window (${Math.round(commentAge / 60000)}m elapsed)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const closeMessage = isPR
|
||||
? 'This pull request has been automatically closed because it was not updated to meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) within the 2-hour window.\n\nFeel free to open a new pull request that follows our guidelines.'
|
||||
: 'This issue has been automatically closed because it was not updated to meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) within the 2-hour window.\n\nFeel free to open a new issue that follows our issue templates.';
|
||||
? 'This pull request has been automatically closed because it was not updated to meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) within the 72-hour window.\n\nFeel free to open a new pull request that follows our guidelines.'
|
||||
: 'This issue has been automatically closed because it was not updated to meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) within the 72-hour window.\n\nFeel free to open a new issue that follows our issue templates.';
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
@@ -129,5 +129,5 @@ jobs:
|
||||
});
|
||||
}
|
||||
|
||||
core.info(`Closed non-compliant ${kind} #${item.number} after 2-hour window`);
|
||||
core.info(`Closed non-compliant ${kind} #${item.number} after 72-hour window`);
|
||||
}
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
|
||||
If the issue is NOT compliant and the author association is not OWNER or MEMBER, start the comment with:
|
||||
<!-- issue-compliance -->
|
||||
Then explain what needs to be fixed and that they have 2 hours to edit the issue before it is automatically closed. Also add the label needs:compliance to the issue using: gh issue edit ${{ github.event.issue.number }} --add-label needs:compliance
|
||||
Then explain what needs to be fixed and that they have 72 hours to edit the issue before it is automatically closed. Also add the label needs:compliance to the issue using: gh issue edit ${{ github.event.issue.number }} --add-label needs:compliance
|
||||
|
||||
If duplicates were found, include a section about potential duplicates with links.
|
||||
|
||||
@@ -124,7 +124,7 @@ jobs:
|
||||
**What needs to be fixed:**
|
||||
- [specific reasons]
|
||||
|
||||
Please edit this issue to address the above within **2 hours**, or it will be automatically closed.
|
||||
Please edit this issue to address the above within **72 hours**, or it will be automatically closed.
|
||||
|
||||
[If duplicates found, add:]
|
||||
---
|
||||
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
echo "=== Flake structure ==="
|
||||
nix flake show --all-systems
|
||||
|
||||
SYSTEMS="x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin"
|
||||
SYSTEMS="x86_64-linux aarch64-linux aarch64-darwin"
|
||||
PACKAGES="opencode"
|
||||
# TODO: move 'desktop' to PACKAGES when #11755 is fixed
|
||||
OPTIONAL_PACKAGES="desktop"
|
||||
|
||||
@@ -34,8 +34,6 @@ jobs:
|
||||
runner: blacksmith-4vcpu-ubuntu-2404
|
||||
- system: aarch64-linux
|
||||
runner: blacksmith-4vcpu-ubuntu-2404-arm
|
||||
- system: x86_64-darwin
|
||||
runner: macos-15-intel
|
||||
- system: aarch64-darwin
|
||||
runner: macos-latest
|
||||
runs-on: ${{ matrix.runner }}
|
||||
@@ -126,7 +124,7 @@ jobs:
|
||||
|
||||
[ -f "$HASH_FILE" ] || echo '{"nodeModules":{}}' > "$HASH_FILE"
|
||||
|
||||
for SYSTEM in x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin; do
|
||||
for SYSTEM in x86_64-linux aarch64-linux aarch64-darwin; do
|
||||
FILE="hashes/hash-${SYSTEM}/hash.txt"
|
||||
if [ -f "$FILE" ]; then
|
||||
HASH="$(tr -d '[:space:]' < "$FILE")"
|
||||
|
||||
@@ -307,7 +307,7 @@ jobs:
|
||||
**What needs to be fixed:**
|
||||
${issues.map(i => `- ${i}`).join('\n')}
|
||||
|
||||
Please edit this PR description to address the above within **2 hours**, or it will be automatically closed.
|
||||
Please edit this PR description to address the above within **72 hours**, or it will be automatically closed.
|
||||
|
||||
If you believe this was flagged incorrectly, please let a maintainer know.`;
|
||||
|
||||
|
||||
@@ -94,12 +94,6 @@ jobs:
|
||||
git config --global user.email "bot@opencode.ai"
|
||||
git config --global user.name "opencode"
|
||||
|
||||
- name: Install ffmpeg
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes ffmpeg
|
||||
|
||||
- name: Cache Turbo
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
@@ -251,6 +245,13 @@ jobs:
|
||||
run: bunx playwright test --config e2e/service-worker/playwright.config.ts
|
||||
timeout-minutes: 5
|
||||
|
||||
- name: Run session UI component tests
|
||||
if: ${{ !cancelled() && env.E2E_ENABLED == 'true' }}
|
||||
run: bun --cwd packages/session-ui test:components
|
||||
env:
|
||||
CI: true
|
||||
timeout-minutes: 15
|
||||
|
||||
- name: Upload Playwright artifacts
|
||||
if: always() && env.E2E_ENABLED == 'true'
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
@@ -261,3 +262,5 @@ jobs:
|
||||
path: |
|
||||
packages/app/e2e/test-results
|
||||
packages/app/e2e/playwright-report
|
||||
packages/session-ui/component-tests/test-results
|
||||
packages/session-ui/component-tests/playwright-report
|
||||
+94
-1
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/nicolo-ribaudo/oxc-project.github.io/refs/heads/json-schema/src/public/.oxlintrc.schema.json",
|
||||
"jsPlugins": [
|
||||
{ "name": "anti-slop", "specifier": "./script/oxlint/anti-slop/index.ts" },
|
||||
{ "name": "anti-slop-effect", "specifier": "./script/oxlint/anti-slop/effect/index.ts" }
|
||||
],
|
||||
"categories": {
|
||||
"correctness": "off",
|
||||
"suspicious": "off",
|
||||
@@ -18,5 +22,94 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ignorePatterns": ["**/node_modules", "**/dist", "**/.build", "**/.sst", "**/*.d.ts", "**/sdk.gen.ts"]
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"packages/app/**",
|
||||
"packages/desktop/**",
|
||||
"packages/gui-extensions/**",
|
||||
"packages/ui/**",
|
||||
"packages/session-ui/**"
|
||||
],
|
||||
"rules": {
|
||||
"oxc/no-accumulating-spread": "warn",
|
||||
"anti-slop/no-array-filter-map": "warn",
|
||||
"anti-slop/no-reduce-accumulator-copy": "warn",
|
||||
"anti-slop/no-chained-type-assertions": "warn",
|
||||
"anti-slop/no-conditional-empty-object-spread": "warn",
|
||||
"anti-slop/no-known-value-widening": "warn",
|
||||
"anti-slop/no-module-mocking": "warn",
|
||||
"anti-slop/no-object-parameters": "warn",
|
||||
"anti-slop/no-reflect-apply": "warn",
|
||||
"anti-slop/no-reflect-get": "warn",
|
||||
"anti-slop/no-runtime-typeof": "warn",
|
||||
"anti-slop/no-shape-in-symbol-names": "warn",
|
||||
"anti-slop/no-unknown-parameters": "warn",
|
||||
"anti-slop/no-unknown-returns": "warn",
|
||||
"anti-slop/no-unknown-type-aliases": "warn",
|
||||
"anti-slop/no-unsafe-dictionary-type": "warn",
|
||||
"anti-slop/no-widen-then-assert": "warn",
|
||||
"anti-slop/require-readable-spacing": "warn",
|
||||
"anti-slop/require-safety-comment-for-type-assertion": "warn"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["packages/app/**", "packages/desktop/**", "packages/gui-extensions/**", "packages/session-ui/**"],
|
||||
"rules": {
|
||||
"anti-slop-effect/no-manual-effect-error-tag": "warn",
|
||||
"anti-slop-effect/no-manual-tag-comparison": "warn",
|
||||
"anti-slop-effect/no-manual-tagged-construction": "warn",
|
||||
"anti-slop-effect/no-service-constructor-imports": "warn",
|
||||
"anti-slop-effect/prefer-effect-match": "warn"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["packages/gui-extensions/src/*/**"],
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"patterns": [
|
||||
{
|
||||
"regex": "^@opencode/(app|desktop)(/|$)",
|
||||
"message": "GUI extensions never import the app or desktop packages. Use the SDK."
|
||||
},
|
||||
{
|
||||
"regex": "^@/",
|
||||
"message": "GUI extensions never import app internals. Use the SDK."
|
||||
},
|
||||
{
|
||||
"group": ["../*/*", "!../*/contract", "!../sdk/*"],
|
||||
"message": "Import another extension only through its contract.ts."
|
||||
},
|
||||
{
|
||||
"regex": "\\.css$",
|
||||
"message": "Import CSS with ?inline and contribute it with ctx.add(Style, css)."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"**/node_modules",
|
||||
"**/dist",
|
||||
"**/.build",
|
||||
"**/.sst",
|
||||
"**/*.d.ts",
|
||||
"**/sdk.gen.ts",
|
||||
".agent/**",
|
||||
".agents/**",
|
||||
".claude/**",
|
||||
".codex/**",
|
||||
".continue/**",
|
||||
".cursor/**",
|
||||
".gemini/**",
|
||||
".opencode/**",
|
||||
".pi/**",
|
||||
".roo/**",
|
||||
".windsurf/**",
|
||||
"script/oxlint/anti-slop/**"
|
||||
]
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
"@actions/artifact": "5.0.1",
|
||||
"@ast-grep/cli": "0.44.0",
|
||||
"@opencode/client": "workspace:*",
|
||||
"@oxlint/plugins": "1.60.0",
|
||||
"@tsconfig/bun": "catalog:",
|
||||
"@types/mime-types": "3.0.1",
|
||||
"@types/react": "19.2.17",
|
||||
@@ -32,7 +33,7 @@
|
||||
},
|
||||
"packages/ai": {
|
||||
"name": "@opencode/ai",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@aws-sdk/credential-providers": "3.1057.0",
|
||||
"@opencode/schema": "workspace:*",
|
||||
@@ -54,7 +55,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode/app",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@corvu/drawer": "catalog:",
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
@@ -64,7 +65,7 @@
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode/client": "workspace:*",
|
||||
"@opencode/plugin-browser": "workspace:*",
|
||||
"@opencode/gui-extensions": "workspace:*",
|
||||
"@opencode/schema": "workspace:*",
|
||||
"@opencode/session-ui": "workspace:*",
|
||||
"@opencode/ui": "workspace:*",
|
||||
@@ -86,13 +87,11 @@
|
||||
"core-js": "3.50.0",
|
||||
"effect": "catalog:",
|
||||
"fuzzysort": "catalog:",
|
||||
"ghostty-web": "github:anomalyco/ghostty-web#83c0a07b8628b748aed073b232cb4b52a6ca11c1",
|
||||
"qr-scanner": "1.4.2",
|
||||
"remeda": "catalog:",
|
||||
"solid-js": "catalog:",
|
||||
"solid-presence": "0.2.0",
|
||||
"tailwindcss": "4.3.3",
|
||||
"uqr": "0.1.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@happy-dom/global-registrator": "20.0.11",
|
||||
@@ -103,6 +102,7 @@
|
||||
"@types/node": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"diff": "catalog:",
|
||||
"ghostty-web": "github:anomalyco/ghostty-web#83c0a07b8628b748aed073b232cb4b52a6ca11c1",
|
||||
"happy-dom": "20.11.1",
|
||||
"tw-animate-css": "1.4.0",
|
||||
"vite": "8.2.2",
|
||||
@@ -112,13 +112,13 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@opencode/cli",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode.cjs",
|
||||
"opencode2": "./bin/opencode2.cjs",
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "1.2.1",
|
||||
"@agentclientprotocol/sdk": "1.6.0",
|
||||
"@clack/core": "1.0.0-alpha.1",
|
||||
"@clack/prompts": "1.0.0-alpha.1",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -178,7 +178,7 @@
|
||||
},
|
||||
"packages/client": {
|
||||
"name": "@opencode/client",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@opencode/protocol": "workspace:*",
|
||||
"@opencode/schema": "workspace:*",
|
||||
@@ -204,7 +204,7 @@
|
||||
},
|
||||
"packages/codemode": {
|
||||
"name": "@opencode/codemode",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"acorn": "8.15.0",
|
||||
"effect": "catalog:",
|
||||
@@ -217,7 +217,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode/console-app",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -253,7 +253,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode/console-core",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -280,7 +280,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode/console-function",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@opencode/console-core": "workspace:*",
|
||||
@@ -297,7 +297,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode/console-mail",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -321,7 +321,7 @@
|
||||
},
|
||||
"packages/console/support": {
|
||||
"name": "@opencode/console-support",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@opencode/console-core": "workspace:*",
|
||||
@@ -341,7 +341,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@opencode/core",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@ai-sdk/cohere": "3.0.27",
|
||||
"@ai-sdk/gateway": "3.0.104",
|
||||
@@ -410,7 +410,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode/desktop",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@zip.js/zip.js": "2.7.62",
|
||||
"electron-context-menu": "5.0.0",
|
||||
@@ -424,26 +424,22 @@
|
||||
"@lydell/node-pty": "catalog:",
|
||||
"@opencode/app": "workspace:*",
|
||||
"@opencode/client": "workspace:*",
|
||||
"@opencode/plugin-browser": "workspace:*",
|
||||
"@opencode/gui-extensions": "workspace:*",
|
||||
"@opencode/schema": "workspace:*",
|
||||
"@opencode/ui": "workspace:*",
|
||||
"@sentry/solid": "catalog:",
|
||||
"@sentry/vite-plugin": "catalog:",
|
||||
"@solid-primitives/storage": "catalog:",
|
||||
"@solidjs/meta": "catalog:",
|
||||
"@solidjs/router": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"app-builder-lib": "26.15.7",
|
||||
"devtools-protocol": "0.0.1687809",
|
||||
"drizzle-kit": "catalog:",
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
"electron": "44.4.5",
|
||||
"electron-builder": "26.15.7",
|
||||
"electron-vite": "6.0.0-beta.1",
|
||||
"puppeteer-core": "25.9.0",
|
||||
"solid-js": "catalog:",
|
||||
"typescript": "~5.6.2",
|
||||
"vite": "8.2.2",
|
||||
@@ -459,7 +455,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode/enterprise",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@hono/standard-validator": "catalog:",
|
||||
"@opencode-ai/sdk": "1.18.21",
|
||||
@@ -496,7 +492,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode/function",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -510,9 +506,54 @@
|
||||
"typescript": "catalog:",
|
||||
},
|
||||
},
|
||||
"packages/gui-extensions": {
|
||||
"name": "@opencode/gui-extensions",
|
||||
"version": "2.0.20",
|
||||
"dependencies": {
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
"@dnd-kit/dom": "0.5.0",
|
||||
"@dnd-kit/solid": "0.5.0",
|
||||
"@effect/platform-node": "catalog:",
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode/client": "workspace:*",
|
||||
"@opencode/plugin-browser": "workspace:*",
|
||||
"@opencode/schema": "workspace:*",
|
||||
"@opencode/session-ui": "workspace:*",
|
||||
"@opencode/ui": "workspace:*",
|
||||
"@opencode/util": "workspace:*",
|
||||
"@pierre/trees": "1.0.0-beta.4",
|
||||
"@solid-primitives/event-listener": "catalog:",
|
||||
"@solid-primitives/keyed": "1.5.3",
|
||||
"@solid-primitives/media": "catalog:",
|
||||
"@solid-primitives/resize-observer": "catalog:",
|
||||
"@solid-primitives/scheduled": "1.5.3",
|
||||
"@tanstack/solid-query": "5.91.4",
|
||||
"@tanstack/solid-virtual": "catalog:",
|
||||
"effect": "catalog:",
|
||||
"fuzzysort": "catalog:",
|
||||
"ghostty-web": "github:anomalyco/ghostty-web#83c0a07b8628b748aed073b232cb4b52a6ca11c1",
|
||||
"remeda": "catalog:",
|
||||
"solid-js": "catalog:",
|
||||
"solid-presence": "0.2.0",
|
||||
"uqr": "0.1.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@happy-dom/global-registrator": "20.0.11",
|
||||
"@lydell/node-pty": "catalog:",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
"@types/bun": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"devtools-protocol": "0.0.1687809",
|
||||
"electron": "44.4.5",
|
||||
"electron-updater": "6.8.9",
|
||||
"lighthouse": "13.4.1",
|
||||
"vite": "catalog:",
|
||||
},
|
||||
},
|
||||
"packages/http-recorder": {
|
||||
"name": "@opencode/http-recorder",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@effect/platform-node-shared": "4.0.0-rc.112",
|
||||
},
|
||||
@@ -531,7 +572,7 @@
|
||||
},
|
||||
"packages/httpapi-codegen": {
|
||||
"name": "@opencode/httpapi-codegen",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"effect": "catalog:",
|
||||
"prettier": "3.6.2",
|
||||
@@ -544,7 +585,7 @@
|
||||
},
|
||||
"packages/latex": {
|
||||
"name": "@opencode/latex",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@opencode/plugin": "workspace:*",
|
||||
"@opentui/core": "catalog:",
|
||||
@@ -558,7 +599,7 @@
|
||||
},
|
||||
"packages/merman": {
|
||||
"name": "@opencode/merman",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@opencode/plugin": "workspace:*",
|
||||
"@opentui/core": "catalog:",
|
||||
@@ -573,7 +614,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode/plugin",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@ai-sdk/provider": "3.0.8",
|
||||
"@opencode/ai": "workspace:*",
|
||||
@@ -599,8 +640,8 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opencode/theme": "workspace:*",
|
||||
"@opentui/core": ">=0.5.13",
|
||||
"@opentui/solid": ">=0.5.13",
|
||||
"@opentui/core": ">=0.5.14",
|
||||
"@opentui/solid": ">=0.5.14",
|
||||
"solid-js": ">=1.9.0",
|
||||
},
|
||||
"optionalPeers": [
|
||||
@@ -612,7 +653,7 @@
|
||||
},
|
||||
"packages/plugin-browser": {
|
||||
"name": "@opencode/plugin-browser",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@opencode/plugin": "workspace:*",
|
||||
"@opencode/schema": "workspace:*",
|
||||
@@ -642,7 +683,7 @@
|
||||
},
|
||||
"packages/protocol": {
|
||||
"name": "@opencode/protocol",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@opencode/schema": "workspace:*",
|
||||
"effect": "catalog:",
|
||||
@@ -657,7 +698,7 @@
|
||||
},
|
||||
"packages/schema": {
|
||||
"name": "@opencode/schema",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -681,7 +722,7 @@
|
||||
},
|
||||
"packages/sdk": {
|
||||
"name": "@opencode/sdk",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@opencode/client": "workspace:*",
|
||||
"@opencode/core": "workspace:*",
|
||||
@@ -702,7 +743,7 @@
|
||||
},
|
||||
"packages/server": {
|
||||
"name": "@opencode/server",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@effect/platform-node": "catalog:",
|
||||
"@effect/platform-node-shared": "catalog:",
|
||||
@@ -724,7 +765,7 @@
|
||||
},
|
||||
"packages/session-ui": {
|
||||
"name": "@opencode/session-ui",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode/client": "workspace:*",
|
||||
@@ -759,7 +800,7 @@
|
||||
},
|
||||
"packages/simulation": {
|
||||
"name": "@opencode/simulation",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@opencode/ai": "workspace:*",
|
||||
"@opencode/core": "workspace:*",
|
||||
@@ -779,7 +820,7 @@
|
||||
},
|
||||
"packages/stats/app": {
|
||||
"name": "@opencode/stats-app",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
@@ -813,7 +854,7 @@
|
||||
},
|
||||
"packages/stats/core": {
|
||||
"name": "@opencode/stats-core",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-athena": "3.933.0",
|
||||
"@planetscale/database": "1.19.0",
|
||||
@@ -832,7 +873,7 @@
|
||||
},
|
||||
"packages/stats/server": {
|
||||
"name": "@opencode/stats-server",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-firehose": "3.933.0",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -878,7 +919,7 @@
|
||||
},
|
||||
"packages/theme": {
|
||||
"name": "@opencode/theme",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@opentui/core": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -892,7 +933,7 @@
|
||||
},
|
||||
"packages/tui": {
|
||||
"name": "@opencode/tui",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@opencode/client": "workspace:*",
|
||||
"@opencode/core": "workspace:*",
|
||||
@@ -926,7 +967,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode/ui",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@pierre/diffs": "catalog:",
|
||||
@@ -961,7 +1002,7 @@
|
||||
},
|
||||
"packages/util": {
|
||||
"name": "@opencode/util",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@effect/opentelemetry": "catalog:",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -999,7 +1040,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode/web",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -1040,7 +1081,7 @@
|
||||
},
|
||||
"services/update": {
|
||||
"name": "@opencode/update",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"dependencies": {
|
||||
"jose": "6.0.11",
|
||||
"semver": "catalog:",
|
||||
@@ -1117,9 +1158,9 @@
|
||||
"@npmcli/arborist": "9.4.0",
|
||||
"@octokit/rest": "22.0.0",
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@opentui/core": "0.5.13",
|
||||
"@opentui/keymap": "0.5.13",
|
||||
"@opentui/solid": "0.5.13",
|
||||
"@opentui/core": "0.5.14",
|
||||
"@opentui/keymap": "0.5.14",
|
||||
"@opentui/solid": "0.5.14",
|
||||
"@pierre/diffs": "1.5.1",
|
||||
"@playwright/test": "1.59.1",
|
||||
"@sentry/solid": "10.71.0",
|
||||
@@ -1189,7 +1230,7 @@
|
||||
|
||||
"@adobe/css-tools": ["@adobe/css-tools@4.5.0", "", {}, "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q=="],
|
||||
|
||||
"@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@1.2.1", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-jwYUdOQR7tc+Zfch53VL4JJyUNK/46q03uUTYb+PjECsmnNl94XFXOfYLJ8RBpMNidXd1rpOAVgb0vqD98xImA=="],
|
||||
"@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@1.6.0", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-XxXrmX7aZkDgOB0Rg9cu+ZFyiUUc5lF2n9seO3Gc4OR+MTdfZOwIqF6m3LvsmmM8K3qgPmXkDH8/IFM2u9vdcQ=="],
|
||||
|
||||
"@ai-sdk/cohere": ["@ai-sdk/cohere@3.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OqcCq2PiFY1dbK/0Ck45KuvE8jfdxRuuAE9Y5w46dAk6U+9vPOeg1CDcmR+ncqmrYrhRl3nmyDttyDahyjCzAw=="],
|
||||
|
||||
@@ -2175,6 +2216,8 @@
|
||||
|
||||
"@opencode/function": ["@opencode/function@workspace:packages/function"],
|
||||
|
||||
"@opencode/gui-extensions": ["@opencode/gui-extensions@workspace:packages/gui-extensions"],
|
||||
|
||||
"@opencode/http-recorder": ["@opencode/http-recorder@workspace:packages/http-recorder"],
|
||||
|
||||
"@opencode/httpapi-codegen": ["@opencode/httpapi-codegen@workspace:packages/httpapi-codegen"],
|
||||
@@ -2255,25 +2298,27 @@
|
||||
|
||||
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.43.0", "", {}, "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg=="],
|
||||
|
||||
"@opentui/core": ["@opentui/core@0.5.13", "", { "dependencies": { "bun-ffi-structs": "0.3.1", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.5.13", "@opentui/core-darwin-x64": "0.5.13", "@opentui/core-linux-arm64": "0.5.13", "@opentui/core-linux-arm64-musl": "0.5.13", "@opentui/core-linux-x64": "0.5.13", "@opentui/core-linux-x64-musl": "0.5.13", "@opentui/core-win32-arm64": "0.5.13", "@opentui/core-win32-x64": "0.5.13" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-3XNXJx3J9I+Ce170oh7GJ3leBZZA7QYv4OAhwiMCPQtKMkhy3Wra99NM26HXcEGme0zjkkJLownwLLnxuc0U/Q=="],
|
||||
"@opentui/core": ["@opentui/core@0.5.14", "", { "dependencies": { "bun-ffi-structs": "0.3.1", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.5.14", "@opentui/core-darwin-x64": "0.5.14", "@opentui/core-linux-arm64": "0.5.14", "@opentui/core-linux-arm64-musl": "0.5.14", "@opentui/core-linux-x64": "0.5.14", "@opentui/core-linux-x64-musl": "0.5.14", "@opentui/core-win32-arm64": "0.5.14", "@opentui/core-win32-x64": "0.5.14" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-tfQ+PWQyeBnYloB3diEcPqbILv14xemH5jjAEICfPuyNDtGBqrjhUtThrhbvFRuPMcj6IEeXrAk6VE8e91h0kg=="],
|
||||
|
||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.5.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5X9U2v5qn4+vKj31zpn0RMoBV3Q9ttdM4t5KN41NPg6XgtIOZgKwox5yOsvBaiQ1KGbuk5wMvXCxfWJ30Mj/nw=="],
|
||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.5.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wWmw41wRMBoI0lN9mgyzGRwYujwwfNkBP6jYM99k4Bx2XkeSZ3sCeu5bwX9vEJPYqjiBShbDhc0NnVSP7NwzVg=="],
|
||||
|
||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.5.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-9IgQHLKSn3quVsU0gibkRjOPegv1QH4573KWYXQNDmSxBXw84Nd1mDNsbK9f72SblAILhOCYK685pmk3X/z0Ig=="],
|
||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.5.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-7smHKDH8IhUaBsgYuAClMl2mHWu+yjpMrtdw3wEvBVtCzKMrHi/TJ5PtydO+IfUzR3BXpVubdbR1irD8BTcR/w=="],
|
||||
|
||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.5.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-jMXGEpB/2Ch/IR0QzYBM5eCr1tUzicxoTP1AbMCbOduJXcGRCHTWqIdQIgRErwPF92gBcDIs4JugSn/Jd0Ki7g=="],
|
||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.5.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-xH1hP+NaLySEJeZkl21NlkZBMddMfQ1jU8NeX1AEBc2GNBOvDXU4Ud/xw87SrAvU1xG9K7/9C4oy4AmIMEpVGg=="],
|
||||
|
||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.5.13", "", { "os": "linux", "cpu": "x64" }, "sha512-7ytcKy7pQxvQer3PADGYNK+HRhSDyhZvp475GRBEhlBJIdhdL8PNU6hjd2R1dnieifiW7Mpc4EiC1kGKe69gYg=="],
|
||||
"@opentui/core-linux-arm64-musl": ["@opentui/core-linux-arm64-musl@0.5.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-mnBBAuTb92NiRLAjOD755tS8/tNQemDztbg9tMvoCT90G52FtVrRb31Ge6OrYqfm0c9DkZGhEBOhunsId/4zSA=="],
|
||||
|
||||
"@opentui/core-linux-x64-musl": ["@opentui/core-linux-x64-musl@0.5.13", "", { "os": "linux", "cpu": "x64" }, "sha512-C2ZC7WA7YH/GS+rJnbeVGe6Kxeor0aTD2qcS6l32KmN4XuY7TXm192XJMfxxnKaTA6oKp3mW27ezolJKUcGtMw=="],
|
||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.5.14", "", { "os": "linux", "cpu": "x64" }, "sha512-Hkk4kaDGMcn9bmJFJPW3/QOGGbPuWe3sCFV/CkiVb4+bCvcTm7EXQr4QTAA63BYy2dKE5bUFUi1zZlyeMkWpnA=="],
|
||||
|
||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.5.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-IB6eLh6I/gYXrJ0AYxZpD+Dr+yfZJck0ZfLnIapbD+fykaT0umOtvc/BL+CtlC/aSc7jpe0q4eXdn6XIA8HtLw=="],
|
||||
"@opentui/core-linux-x64-musl": ["@opentui/core-linux-x64-musl@0.5.14", "", { "os": "linux", "cpu": "x64" }, "sha512-ngJ+U2grOGEteeQvZdAJNqn09M+At5mfWInauK5aS427bea1yLo+e6hor/CRmbn9SxEEF+SwoyekaOrLPWyU7w=="],
|
||||
|
||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.5.13", "", { "os": "win32", "cpu": "x64" }, "sha512-FCucDN8A4iFDPdwqLK99ISs42pcbcObkxhN1jtsnmcYM9/Ptron7Hhapx2ObQWYjSrJNnntmfZQd2ewpE8YqTQ=="],
|
||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.5.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-T9kNqKXg2jysmTsyyZ1A8LBQotFBM+iPjzyRslxyexqrs8a1UcmxbApEo+COtxQuqukMTbvwyqEdAT8vcmxEkQ=="],
|
||||
|
||||
"@opentui/keymap": ["@opentui/keymap@0.5.13", "", { "dependencies": { "@opentui/core": "0.5.13" }, "peerDependencies": { "@opentui/react": "0.5.13", "@opentui/solid": "0.5.13", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-bbpQ5N8000CuvVDhptGuHxUE92sivChjUjLkvYT1VnLlDTiGbQ4mPsHfnqsaEokc7O7jBcoJXJQsLxw7rO/qkg=="],
|
||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.5.14", "", { "os": "win32", "cpu": "x64" }, "sha512-mqKSkab8VdMLSmMdocna7+BTyMoIutkVXOV9lfmPrBO2g7Np5c6c4SJ4QIVZqPvast11XyT0/7FfXOYLKAS72w=="],
|
||||
|
||||
"@opentui/solid": ["@opentui/solid@0.5.13", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.5.13", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-MGMYQYeoZNn9uq3zsLPqq/G+nBQXgGwpylsrYux0nJ+C+6oflDEE6t8huSTZaAUizC/3WG8sVSVFutczeRbnPg=="],
|
||||
"@opentui/keymap": ["@opentui/keymap@0.5.14", "", { "dependencies": { "@opentui/core": "0.5.14" }, "peerDependencies": { "@opentui/react": "0.5.14", "@opentui/solid": "0.5.14", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-YGTAvRrpQTbRNV7GH0UxRuCSPxwnSooVdB+qPHHP+3ywc93+lhekljgP8dRivl32f3CiMqqm96Uj2PgwKxkC0Q=="],
|
||||
|
||||
"@opentui/solid": ["@opentui/solid@0.5.14", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.5.14", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-bBRl34mZ0wFGhjHX6y1VNiZmJ3DSm2DVPm0PNSVDpvq9qxRHuywEPTCn/Lgte0C9450tgf11cDqcE3pR9cZwVA=="],
|
||||
|
||||
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
|
||||
|
||||
@@ -2475,6 +2520,8 @@
|
||||
|
||||
"@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.60.0", "", { "os": "win32", "cpu": "x64" }, "sha512-JOro4ZcfBLamJCyfURQmOQByoorgOdx3ZjAkSqnb/CyG/i+lN3KoV5LAgk5ZAW6DPq7/Cx7n23f8DuTWXTWgyQ=="],
|
||||
|
||||
"@oxlint/plugins": ["@oxlint/plugins@1.60.0", "", {}, "sha512-wxEoVVAS5FQAXiA8jPY+NWTOTKTzbSViMfW7yU5OQ7+f34W0KYfy2iuSLC9o1fiYn5xt3LQLxZBREFJtDlQLRw=="],
|
||||
|
||||
"@pagefind/darwin-arm64": ["@pagefind/darwin-arm64@1.5.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MXpI+7HsAdPkvJ0gk9xj9g541BCqBZOBbdwj9g6lB5LCj6kSV6nqDSjzcAJwvOsfu0fjwvC8hQU+ecfhp+MpiQ=="],
|
||||
|
||||
"@pagefind/darwin-x64": ["@pagefind/darwin-x64@1.5.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-IojxFWMEJe0RQ7PQ3KXQsPIImNsbpPYpoZ+QUDrL8fAl/O27IX+LVLs74/UzEZy5uA2LD8Nz1AiwKr72vrkZQw=="],
|
||||
@@ -6197,6 +6244,8 @@
|
||||
|
||||
"@opencode/files/wrangler": ["wrangler@4.110.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "4.20260708.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260708.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^5.20260708.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js", "cf-wrangler": "bin/cf-wrangler.js" } }, "sha512-xZeXKYi7hxQRF5anL+v77RkufJNpF9f3Eqeyqq2QBsETpLZgh0Agj0jJ6JPtkbgn6ukZdh8OK5egsGPWIditgg=="],
|
||||
|
||||
"@opencode/gui-extensions/vite": ["vite@7.3.6", "", { "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg=="],
|
||||
|
||||
"@opencode/posts/wrangler": ["wrangler@4.110.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "4.20260708.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260708.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^5.20260708.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js", "cf-wrangler": "bin/cf-wrangler.js" } }, "sha512-xZeXKYi7hxQRF5anL+v77RkufJNpF9f3Eqeyqq2QBsETpLZgh0Agj0jJ6JPtkbgn6ukZdh8OK5egsGPWIditgg=="],
|
||||
|
||||
"@opencode/session-ui/vite": ["vite@7.3.6", "", { "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg=="],
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
"aarch64-linux"
|
||||
"x86_64-linux"
|
||||
"aarch64-darwin"
|
||||
"x86_64-darwin"
|
||||
];
|
||||
forEachSystem = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
|
||||
rev = self.shortRev or self.dirtyShortRev or "dirty";
|
||||
@@ -22,7 +21,7 @@
|
||||
default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
bun
|
||||
nodejs_20
|
||||
nodejs
|
||||
pkg-config
|
||||
openssl
|
||||
git
|
||||
|
||||
@@ -7,7 +7,6 @@ in
|
||||
aarch64-linux = "20e68d6c4e47f3ebf59de7c6b1f8b8bec6a6ebda6a451132f9b465f3f13ce467";
|
||||
x86_64-linux = "2452b27112d92387471fa2488aafac85d79ea3f2ee1216c0abd5150d6c12362b";
|
||||
aarch64-darwin = "ac7194a3dfd81930ba35355c01620262c1254752859b42dcb8f4b9e4d174a871";
|
||||
x86_64-darwin = "4489aba55477a0082266cb690db1c829503ba3338048599d8fd243953df37dab";
|
||||
# fetchzip hashes the unpacked headers, not the release tarball.
|
||||
headers = "sha256-4eUy3BZVvxTl7KUOsxio7769lL6ag/ecbeK+qLURWMI=";
|
||||
}
|
||||
+3
-4
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"nodeModules": {
|
||||
"x86_64-linux": "sha256-7DgxTpKv6ITKTom0mhlJNPQfdEjtEHK5P8uyCr26HZw=",
|
||||
"aarch64-linux": "sha256-PJxW1Ibfx6oS1neWPSHqzP1Pm1HT9my1TrrHmOb6/no=",
|
||||
"aarch64-darwin": "sha256-VIme5VHfM8JxNiDSOykkr5FytghDLI0FxkhiOXUSyQw=",
|
||||
"x86_64-darwin": "sha256-rQ/j0QkR1vxAq4jgUbr0nY4RDyiLTJqN8q1AfoiEqVQ="
|
||||
"x86_64-linux": "sha256-g3k0cAFGqzmRYlcIkg1NDvlx1WxHYhnYPL0/a8E+qTg=",
|
||||
"aarch64-linux": "sha256-a+3ymqdxOONGe2Tpq4GUccl1b+Dwzxlb9LFXgE1gZ+0=",
|
||||
"aarch64-darwin": "sha256-h8xIzuMmaWfJqjHCO74xUDCWNKQLFrIGoKYZ+2TauYc="
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,5 @@ stdenvNoCC.mkDerivation {
|
||||
"aarch64-linux"
|
||||
"x86_64-linux"
|
||||
"aarch64-darwin"
|
||||
"x86_64-darwin"
|
||||
];
|
||||
}
|
||||
+8
-7
@@ -2,7 +2,7 @@
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "opencode",
|
||||
"description": "AI-powered development tool",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "bun@1.4.2",
|
||||
@@ -18,7 +18,7 @@
|
||||
"dev:www": "bun run --cwd services/www dev",
|
||||
"dev:storybook": "bun --cwd packages/storybook storybook",
|
||||
"bench:devex": "bun run --cwd packages/app test:bench:devex",
|
||||
"lint": "oxlint",
|
||||
"lint": "oxlint && ast-grep scan -c script/ast-grep/gui-extensions/sgconfig.yml",
|
||||
"lint:effect-patterns": "ast-grep scan -c script/ast-grep/sgconfig.yml packages/util/src packages/core/src packages/server/src packages/protocol/src packages/cli/src",
|
||||
"lint:effect-simplifications": "ast-grep scan -c script/ast-grep/effect-simplifications/sgconfig.yml --off=unused-suppression packages",
|
||||
"test:lint-rules": "ast-grep test -c script/ast-grep/sgconfig.yml",
|
||||
@@ -52,9 +52,9 @@
|
||||
"@octokit/rest": "22.0.0",
|
||||
"@hono/standard-validator": "0.2.0",
|
||||
"@hono/zod-validator": "0.4.2",
|
||||
"@opentui/core": "0.5.13",
|
||||
"@opentui/keymap": "0.5.13",
|
||||
"@opentui/solid": "0.5.13",
|
||||
"@opentui/core": "0.5.14",
|
||||
"@opentui/keymap": "0.5.14",
|
||||
"@opentui/solid": "0.5.14",
|
||||
"@tanstack/solid-virtual": "3.13.37",
|
||||
"@shikijs/stream": "4.4.3",
|
||||
"@standard-schema/spec": "1.1.0",
|
||||
@@ -117,10 +117,11 @@
|
||||
"@actions/artifact": "5.0.1",
|
||||
"@ast-grep/cli": "0.44.0",
|
||||
"@opencode/client": "workspace:*",
|
||||
"@types/react": "19.2.17",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@oxlint/plugins": "1.60.0",
|
||||
"@tsconfig/bun": "catalog:",
|
||||
"@types/mime-types": "3.0.1",
|
||||
"@types/react": "19.2.17",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"glob": "13.0.5",
|
||||
"husky": "9.1.7",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "2.0.20",
|
||||
"version": "2.0.21",
|
||||
"name": "@opencode/ai",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -38,6 +38,7 @@ const resolve = (policy: CachePolicy | undefined): CachePolicyObject => {
|
||||
// prefix caching, Gemini's implicit + out-of-band CachedContent). Skip the
|
||||
// whole policy pass for these — emitting hints would be harmless but pointless.
|
||||
const RESPECTS_INLINE_HINTS = new Set([
|
||||
"alibaba-chat",
|
||||
"alibaba-messages",
|
||||
"anthropic-messages",
|
||||
"anthropic-compatible-messages",
|
||||
@@ -49,16 +50,18 @@ const RESPECTS_INLINE_HINTS = new Set([
|
||||
"zai-coding-messages",
|
||||
"bedrock-converse",
|
||||
"openrouter",
|
||||
"digitalocean",
|
||||
])
|
||||
|
||||
// OpenRouter upstreams other than Anthropic and Alibaba Qwen cache without breakpoints. Gemini uses only the last
|
||||
// breakpoint, so a conversation-tail breakpoint writes a new cache every step and costs more than none. Qwen ignores
|
||||
// breakpoints on tool definitions and caches tools with the system prompt.
|
||||
const QWEN: CachePolicyObject = { system: true, messages: { tail: 1 } }
|
||||
const openRouterPolicy = (modelID: string): CachePolicyObject => {
|
||||
// `~anthropic/claude-sonnet-latest` style IDs are OpenRouter aliases for the latest model in a family.
|
||||
const id = modelID.replace(/^~/, "")
|
||||
if (id.startsWith("anthropic/")) return AUTO
|
||||
if (id.startsWith("qwen/")) return { system: true, messages: { tail: 1 } }
|
||||
if (id.startsWith("qwen/")) return QWEN
|
||||
return NONE
|
||||
}
|
||||
|
||||
@@ -170,7 +173,11 @@ export const applyCachePolicy = (request: LLMRequest): LLMRequest => {
|
||||
const policy =
|
||||
request.model.route.id === "openrouter" && (request.cache === undefined || request.cache === "auto")
|
||||
? openRouterPolicy(request.model.id)
|
||||
: resolve(request.cache)
|
||||
: request.model.route.id === "alibaba-chat" && (request.cache === undefined || request.cache === "auto")
|
||||
? request.model.id.toLowerCase().startsWith("qwen")
|
||||
? QWEN
|
||||
: NONE
|
||||
: resolve(request.cache)
|
||||
if (!policy.tools && !policy.system && !policy.messages) return request
|
||||
|
||||
const hint = makeHint(policy.ttlSeconds)
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Protocol } from "../route/protocol.js"
|
||||
import type { LanguageModelCompatibility } from "../schema/index.js"
|
||||
import { OpenAIChat } from "./openai-chat.js"
|
||||
import { JsonObject, ProviderShared } from "./shared.js"
|
||||
import { cacheControl } from "./utils/cache.js"
|
||||
import { OpenResponsesOptions } from "./utils/open-responses-options.js"
|
||||
|
||||
export type ReasoningEffort = OpenResponsesOptions.ReasoningEffort
|
||||
@@ -68,7 +69,7 @@ export const protocol = Protocol.make({
|
||||
from: Effect.fn("AlibabaChat.fromRequest")(function* (req) {
|
||||
const opts = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Options))(req.providerOptions ?? {})
|
||||
return {
|
||||
...(yield* OpenAIChat.protocol.body.from(req)),
|
||||
...(yield* OpenAIChat.fromRequest(req, { cacheControl: cacheControl() })),
|
||||
enable_thinking: opts.enableThinking,
|
||||
// Alibaba also rejects an explicit budget that is not below `max_completion_tokens`.
|
||||
thinking_budget:
|
||||
|
||||
@@ -451,6 +451,9 @@ const AnthropicStreamDelta = Schema.Struct({
|
||||
signature: Schema.optional(Schema.String),
|
||||
stop_reason: optionalNull(Schema.String),
|
||||
stop_sequence: optionalNull(Schema.String),
|
||||
stop_details: optionalNull(
|
||||
Schema.Struct({ category: optionalNull(Schema.String), explanation: optionalNull(Schema.String) }),
|
||||
),
|
||||
})
|
||||
type AnthropicStreamDelta = Schema.Schema.Type<typeof AnthropicStreamDelta>
|
||||
const decodeAnthropicStreamDelta = Schema.decodeUnknownOption(AnthropicStreamDelta)
|
||||
@@ -804,15 +807,12 @@ const requireThinkingSignature = (request: LLMRequest) => {
|
||||
// Mid-conversation system messages became available with Opus 4.8 and version
|
||||
// 5 of the other supported Claude families. Treat later family versions as
|
||||
// compatible without assuming that every Anthropic Messages model is Claude.
|
||||
// Opus 4.8 and every Claude 5 model accept mid-conversation system messages; later versions inherit support.
|
||||
const supportsNativeSystemUpdates = (request: LLMRequest) => {
|
||||
const match = /(?:^|[./])claude-(fable|haiku|mythos|opus|sonnet)-(\d+)(?:[.-](\d+))?/.exec(
|
||||
String(request.model.id).toLowerCase(),
|
||||
)
|
||||
if (!match) return false
|
||||
const major = Number(match[2])
|
||||
if (match[1] !== "opus") return major >= 5
|
||||
if (major !== 4) return major >= 5
|
||||
return match[3] !== undefined && match[3].length <= 2 && Number(match[3]) >= 8
|
||||
const version = claudeVersion(String(request.model.id))
|
||||
if (version === undefined) return false
|
||||
if (version.family === "opus" && version.major === 4) return version.minor >= 8
|
||||
return version.major >= 5
|
||||
}
|
||||
|
||||
const endsInServerToolUse = (message: LLMRequest["messages"][number]) => {
|
||||
@@ -989,13 +989,13 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (
|
||||
return messages
|
||||
})
|
||||
|
||||
// Per-turn effort started with Claude Opus 5 and every Claude 5.1 model; later versions of any family inherit it.
|
||||
const supportsEffortUpdates = (model: LLMRequest["model"]) => {
|
||||
const override = model.compatibility?.supportsEffortUpdates
|
||||
if (override !== undefined) return override
|
||||
const version = claudeVersion(model.id)
|
||||
if (version === undefined) return false
|
||||
if (version.family === "opus") return version.major >= 5
|
||||
if (version.family !== "fable" && version.family !== "mythos") return false
|
||||
if (version.family === "opus" && version.major >= 5) return true
|
||||
return version.major > 5 || (version.major === 5 && version.minor >= 1)
|
||||
}
|
||||
|
||||
@@ -1417,10 +1417,14 @@ const onMessageDelta = (
|
||||
stopSequence === null || stopSequence === undefined
|
||||
? state.pendingFinish?.providerMetadata
|
||||
: providerMetadata(state.providerMetadataKey, { stopSequence })
|
||||
const category = event.delta?.stop_details?.category
|
||||
const explanation = event.delta?.stop_details?.explanation
|
||||
return {
|
||||
reason: {
|
||||
normalized: mapFinishReason(stopReason),
|
||||
raw: stopReason,
|
||||
...(category ? { category } : {}),
|
||||
...(explanation ? { explanation } : {}),
|
||||
},
|
||||
providerMetadata: finishMetadata,
|
||||
}
|
||||
|
||||
@@ -319,6 +319,9 @@ const lowerToolResult = Effect.fn("BedrockConverse.lowerToolResult")(function* (
|
||||
} satisfies BedrockToolResultBlock
|
||||
})
|
||||
|
||||
// Keep Claude and Nova tool-result images inline; put other models' images beside the result.
|
||||
const keepToolImagesInline = (id: string) => id.includes("anthropic.claude-") || id.includes("amazon.nova-")
|
||||
|
||||
const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (
|
||||
request: LLMRequest,
|
||||
breakpoints: BedrockCache.Breakpoints,
|
||||
@@ -328,8 +331,19 @@ const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (
|
||||
// Mistral can reject replay IDs even when they satisfy Converse's broader ID syntax.
|
||||
const normalizeID = request.model.id.includes("mistral.") ? MistralToolID.normalizer(request) : (id: string) => id
|
||||
const providerMetadataKey = request.model.route.providerMetadataKey ?? String(request.model.provider)
|
||||
const hoistImages = !keepToolImagesInline(request.model.id)
|
||||
// Bedrock expects parallel tool results before any images hoisted beside them.
|
||||
const pendingImages: BedrockMedia.ImageBlock[] = []
|
||||
const flushImages = () => {
|
||||
if (pendingImages.length === 0) return
|
||||
const previous = messages.at(-1)
|
||||
if (previous?.role === "user")
|
||||
messages[messages.length - 1] = { role: "user", content: [...previous.content, ...pendingImages] }
|
||||
pendingImages.length = 0
|
||||
}
|
||||
|
||||
for (const message of request.messages) {
|
||||
if (message.role !== "tool") flushImages()
|
||||
if (message.role === "system") {
|
||||
const part = yield* ProviderShared.wrappedSystemUpdate("Bedrock Converse", message)
|
||||
const content = textWithCache(breakpoints, part.text, part.cache)
|
||||
@@ -403,7 +417,22 @@ const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (
|
||||
for (const part of message.content) {
|
||||
if (!ProviderShared.supportsContent(part, ["tool-result"]))
|
||||
return yield* ProviderShared.unsupportedContent("Bedrock Converse", "tool", ["tool-result"])
|
||||
content.push(yield* lowerToolResult(part, documentNames, normalizeID))
|
||||
const result = yield* lowerToolResult(part, documentNames, normalizeID)
|
||||
const images: BedrockMedia.ImageBlock[] = hoistImages
|
||||
? result.toolResult.content.filter((item) => "image" in item)
|
||||
: []
|
||||
const nonImageContent = result.toolResult.content.filter((item) => !("image" in item))
|
||||
content.push(
|
||||
images.length === 0
|
||||
? result
|
||||
: {
|
||||
toolResult: {
|
||||
...result.toolResult,
|
||||
content: nonImageContent.length > 0 ? nonImageContent : [{ text: "See attached image." }],
|
||||
},
|
||||
},
|
||||
)
|
||||
pendingImages.push(...images)
|
||||
const cachePoint = BedrockCache.block(breakpoints, part.cache)
|
||||
if (cachePoint) content.push(cachePoint)
|
||||
}
|
||||
@@ -413,6 +442,7 @@ const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (
|
||||
else messages.push({ role: "user", content })
|
||||
}
|
||||
|
||||
flushImages()
|
||||
return messages
|
||||
})
|
||||
|
||||
|
||||
@@ -212,9 +212,11 @@ const OpenAIChatUsage = Schema.StructWithRest(
|
||||
prompt_tokens: optionalNull(Schema.Number),
|
||||
completion_tokens: optionalNull(Schema.Number),
|
||||
total_tokens: optionalNull(Schema.Number),
|
||||
// Zai reports cache hits as top-level `cached_tokens`; DeepSeek uses `prompt_cache_hit_tokens`.
|
||||
// Provider-specific cache accounting fields.
|
||||
cached_tokens: optionalNull(Schema.Number),
|
||||
prompt_cache_hit_tokens: optionalNull(Schema.Number),
|
||||
cache_read_input_tokens: optionalNull(Schema.Number),
|
||||
cache_created_input_tokens: optionalNull(Schema.Number),
|
||||
prompt_tokens_details: optionalNull(
|
||||
Schema.StructWithRest(
|
||||
Schema.Struct({
|
||||
@@ -903,16 +905,19 @@ const mapFinishReason = Effect.fn("OpenAIChat.mapFinishReason")(function* (event
|
||||
// satisfied on both sides.
|
||||
// Providers differ on cache-hit location: OpenAI uses
|
||||
// `prompt_tokens_details.cached_tokens`, DeepSeek uses
|
||||
// `prompt_cache_hit_tokens`, and Zai uses top-level `cached_tokens`.
|
||||
// `prompt_cache_hit_tokens`, Zai uses top-level `cached_tokens`, and
|
||||
// DigitalOcean uses top-level `cache_read_input_tokens` / `cache_created_input_tokens`.
|
||||
const mapUsage = (usage: OpenAIChatEvent["usage"], providerMetadataKey: string): Usage | undefined => {
|
||||
if (!usage) return undefined
|
||||
const input = usage.prompt_tokens ?? undefined
|
||||
const output = usage.completion_tokens ?? undefined
|
||||
const cached = (usage.prompt_tokens_details?.cached_tokens ??
|
||||
(usage as { prompt_cache_hit_tokens?: number | null }).prompt_cache_hit_tokens ??
|
||||
(usage as { cached_tokens?: number | null }).cached_tokens ??
|
||||
undefined) as number | undefined
|
||||
const cacheWrite = usage.prompt_tokens_details?.cache_write_tokens ?? undefined
|
||||
const cached =
|
||||
usage.prompt_tokens_details?.cached_tokens ??
|
||||
usage.prompt_cache_hit_tokens ??
|
||||
usage.cached_tokens ??
|
||||
usage.cache_read_input_tokens ??
|
||||
undefined
|
||||
const cacheWrite = usage.prompt_tokens_details?.cache_write_tokens ?? usage.cache_created_input_tokens ?? undefined
|
||||
const reasoning = usage.completion_tokens_details?.reasoning_tokens ?? undefined
|
||||
const nonCached = ProviderShared.subtractTokens(input, ProviderShared.sumTokens(cached, cacheWrite))
|
||||
return new Usage({
|
||||
|
||||
@@ -143,14 +143,15 @@ const adapter = {
|
||||
restoreHostedToolItem: (item: unknown) => (Schema.is(OpenAIResponsesHostedToolItem)(item) ? item : undefined),
|
||||
} satisfies OpenResponses.ProviderAdapter
|
||||
|
||||
// GPT-6 Astra, Sol, and Luna accept `configuration_update` only in standard mode (not `reasoning.mode: "pro"` or
|
||||
// `-pro` slugs), and never alongside automatic `context_management` compaction.
|
||||
// GPT-6 and later default to `configuration_update` support, except in `reasoning.mode: "pro"`
|
||||
// or alongside automatic `context_management` compaction.
|
||||
const supportsEffortUpdates = (request: LLMRequest) => {
|
||||
if (request.providerOptions?.contextManagement !== undefined) return false
|
||||
if (Schema.is(Schema.Struct({ mode: Schema.Literal("pro") }))(request.http?.body?.reasoning)) return false
|
||||
const override = request.model.compatibility?.supportsEffortUpdates
|
||||
if (override !== undefined) return override
|
||||
return /(?:^|\/)gpt-6-(?:astra|sol|luna)$/i.test(request.model.id)
|
||||
const match = /(?:^|\/)gpt-(\d+)(?:\.\d+)?(?:-|$)/i.exec(request.model.id)
|
||||
return match !== null && Number(match[1]) >= 6
|
||||
}
|
||||
|
||||
const nativeImageToolInput = (tool: ToolDefinition) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Shared counter and TTL mapping for provider cache-marker lowering.
|
||||
import type { CacheHint } from "../../schema/index.js"
|
||||
|
||||
export interface Breakpoints {
|
||||
remaining: number
|
||||
@@ -11,3 +12,12 @@ export const newBreakpoints = (cap: number): Breakpoints => ({ remaining: cap, d
|
||||
// requests omit the wire TTL and use the provider default.
|
||||
export const ttlBucket = (ttlSeconds: number | undefined): "1h" | undefined =>
|
||||
ttlSeconds !== undefined && ttlSeconds >= 3600 ? "1h" : undefined
|
||||
|
||||
export const cacheControl = () => {
|
||||
const breakpoints = newBreakpoints(4)
|
||||
return (cache: CacheHint | undefined) => {
|
||||
if (cache === undefined || breakpoints.remaining === 0) return undefined
|
||||
breakpoints.remaining -= 1
|
||||
return { type: "ephemeral" as const, ttl: ttlBucket(cache.ttlSeconds) }
|
||||
}
|
||||
}
|
||||
@@ -41,16 +41,20 @@ const Body = Schema.Struct({
|
||||
user_id: Options.fields.userID,
|
||||
})
|
||||
|
||||
// Tool streaming was introduced in GLM-4.6; later versions inherit support.
|
||||
const supportsToolStreaming = (modelID: string) => {
|
||||
const match = /(?:^|\/)glm-(\d+)(?:\.(\d+))?(?:-|$)/i.exec(modelID)
|
||||
return match !== null && (Number(match[1]) > 4 || (Number(match[1]) === 4 && Number(match[2] ?? 0) >= 6))
|
||||
}
|
||||
|
||||
const fromRequest = Effect.fn("ZAIChat.fromRequest")(function* (request: LLMRequest) {
|
||||
const options = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Options))(request.providerOptions ?? {})
|
||||
const body = yield* OpenAIChat.protocol.body.from(request)
|
||||
return {
|
||||
...body,
|
||||
thinking: options.thinking,
|
||||
// Tool streaming was introduced in GLM-4.6; older models must not receive the opt-in.
|
||||
tool_stream:
|
||||
options.toolStream ??
|
||||
(body.tools?.length && /^glm-(?:4\.[67]|5(?:[.-]|$))/i.test(request.model.id) ? true : undefined),
|
||||
options.toolStream ?? (body.tools?.length && supportsToolStreaming(request.model.id) ? true : undefined),
|
||||
do_sample: options.doSample,
|
||||
response_format: options.responseFormat,
|
||||
request_id: options.requestID,
|
||||
|
||||
@@ -21,21 +21,29 @@ const patterns = [
|
||||
/exceeds the context window/i,
|
||||
/exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\d,]+ tokens?|\s*\([\d,]+\))/i,
|
||||
/input token count.*exceeds the maximum/i,
|
||||
// Amazon Nova on Bedrock reports this as a mid-stream validationException.
|
||||
/number of input tokens exceeds maximum length/i,
|
||||
/tokens in request more than max tokens allowed/i,
|
||||
/maximum prompt length is \d+/i,
|
||||
/reduce the length of the messages/i,
|
||||
// DeepInfra
|
||||
/requested input length \d+ exceeds maximum input length/i,
|
||||
/maximum context length is \d+ tokens/i,
|
||||
/exceeds (?:the )?maximum allowed input length of [\d,]+ tokens?/i,
|
||||
/input \(\d+ tokens\) is longer than the model'?s context length \(\d+ tokens\)/i,
|
||||
// Novita omits the token counts.
|
||||
/input(?: \(\d+ tokens\))? is longer than the model'?s context length/i,
|
||||
/exceeds the limit of \d+/i,
|
||||
/exceeds the available context size/i,
|
||||
/greater than the context length/i,
|
||||
// Hugging Face Text Generation Inference, e.g. Together
|
||||
/`inputs` tokens \+ `max_new_tokens` must be <= \d+/i,
|
||||
/context window exceeds limit/i,
|
||||
/exceeded model token limit/i,
|
||||
/context[_ ]length[_ ]exceeded/i,
|
||||
/context length is only \d+ tokens/i,
|
||||
/input length.*exceeds.*context length/i,
|
||||
/prompt too long; exceeded (?:max )?context length/i,
|
||||
// Z.ai code 1261 arrives as `Prompt too long` or `Prompt 超长`.
|
||||
/prompt (?:too long|超长)/i,
|
||||
/too large for model with \d+ maximum context length/i,
|
||||
/prompt has [\d,]+ tokens?, but the configured context size is [\d,]+ tokens?/i,
|
||||
/model_context_window_exceeded/i,
|
||||
@@ -112,7 +120,8 @@ const QUOTA_CODES = new Set([
|
||||
"creditlimitexceeded",
|
||||
])
|
||||
// Google reports an invalid API key as HTTP 400 INVALID_ARGUMENT with this `details[].reason`.
|
||||
const AUTH_CODES = new Set(["authentication_error", "permission_error", "api_key_invalid"])
|
||||
// Z.ai's Responses API reports account and plan rejections mid-stream as `permission_denied`.
|
||||
const AUTH_CODES = new Set(["authentication_error", "permission_error", "permission_denied", "api_key_invalid"])
|
||||
const SERVER_CODES = new Set([
|
||||
"api_error",
|
||||
"internal_error",
|
||||
@@ -126,6 +135,7 @@ const SERVER_CODES = new Set([
|
||||
])
|
||||
// `invalid_request` is the Vercel AI Gateway's code for an upstream request rejection.
|
||||
const INVALID_REQUEST_CODES = new Set([
|
||||
"model_not_found",
|
||||
"invalid_prompt",
|
||||
"invalid_request",
|
||||
"invalid_request_error",
|
||||
@@ -145,15 +155,19 @@ const CONTENT_POLICY_CODES = new Set([
|
||||
// OpenCode Zen replaces upstream codes outside its allow-list but keeps the original
|
||||
// as a `[code]` label at the start of the rewritten message.
|
||||
const GATEWAY_CODE_LABEL = /^[^:\n]+: \[([A-Za-z0-9_.-]+)\]/
|
||||
// xAI reports an invalid API key as HTTP 400 with the generic `invalid-argument` code.
|
||||
const AUTH_TEXT = /incorrect api key provided/i
|
||||
const RATE_LIMIT_TEXT = /rate increased too quickly|rate[-_\s]?limit|too[_\s]?many[_\s]?requests/i
|
||||
// Only consulted on 429, where throttles and account caps share a status.
|
||||
const QUOTA_TEXT = /insufficient[-_\s]?quota|quota[-_\s]?exceeded|budget exceeded|usage limit/i
|
||||
// Z.ai reports balance, plan expiry, plan limits, and plan model access on 429.
|
||||
const QUOTA_TEXT =
|
||||
/insufficient[-_\s]?(?:quota|balance)|quota[-_\s]?exceeded|budget exceeded|usage limit|limit exhausted|package has expired|plan does not yet include/i
|
||||
// Policy rejections without a dedicated code, matched against the provider's own
|
||||
// explanation only. OpenAI reuses `invalid_prompt` for usage-policy rejections while
|
||||
// Bedrock Mantle reuses it for schema validation; Anthropic reports blocked output
|
||||
// under `invalid_request_error`.
|
||||
const CONTENT_POLICY_TEXT =
|
||||
/violating our usage policy|blocked by content filtering policy|content[-_\s]?policy|rejected as a result of our safety system/i
|
||||
/violating our usage policy|blocked by content filtering policy|content[-_\s]?policy|rejected as a result of our safety system|detected potentially unsafe or sensitive content/i
|
||||
const SERVER_ERROR_TEXT =
|
||||
/\b(?:try again|(?:please |you can )?retry (?:the |this |your )?request|try (?:the |this |your )?request again|(?:currently |temporarily )?at capacity|overloaded|temporarily unavailable|service[-_\s]?unavailable|(?:server|internal)[-_\s]?error|server (?:is )?busy|provider returned (?:an )?error|resource[-_\s]?exhausted|upstream (?:connect|connection|request)|request buffer limit while retrying upstream)\b/i
|
||||
|
||||
@@ -244,7 +258,12 @@ export function classifyProviderFailure(input: ProviderFailure): AIError["reason
|
||||
(input.status === 429 && QUOTA_TEXT.test(text))
|
||||
)
|
||||
return new QuotaExceededError(details)
|
||||
if (input.status === 401 || input.status === 403 || codes.some((code) => AUTH_CODES.has(code)))
|
||||
if (
|
||||
input.status === 401 ||
|
||||
input.status === 403 ||
|
||||
codes.some((code) => AUTH_CODES.has(code)) ||
|
||||
(input.status === 400 && AUTH_TEXT.test(text))
|
||||
)
|
||||
return new AuthenticationError(details)
|
||||
if (
|
||||
input.status === 429 ||
|
||||
|
||||
@@ -42,14 +42,24 @@ export type Settings = ProviderPackage.Settings &
|
||||
|
||||
export const baseURL = (input: GatewayURL) => {
|
||||
if (input.baseURL) return input.baseURL
|
||||
if (!input.accountId)
|
||||
throw new ProviderConfigurationError({
|
||||
provider: id,
|
||||
message: "CloudflareAIGateway.configure requires accountId unless baseURL is supplied",
|
||||
})
|
||||
return `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(input.accountId)}/ai/v1`
|
||||
return `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(requireAccountId(input))}/ai/v1`
|
||||
}
|
||||
|
||||
// Cloudflare's REST API rejects parts of the Anthropic Messages and OpenAI Responses request shapes, such as
|
||||
// system prompt blocks and `minimal` reasoning effort, so these models use the provider-native gateway endpoints.
|
||||
const passthroughURL = (input: GatewayURL & GatewayOptions, provider: "anthropic/v1" | "openai") =>
|
||||
`https://gateway.ai.cloudflare.com/v1/${encodeURIComponent(requireAccountId(input))}/${encodeURIComponent(gatewayId(input))}/${provider}`
|
||||
|
||||
const requireAccountId = (input: GatewayURL) => {
|
||||
if (input.accountId) return input.accountId
|
||||
throw new ProviderConfigurationError({
|
||||
provider: id,
|
||||
message: "CloudflareAIGateway.configure requires accountId unless baseURL is supplied",
|
||||
})
|
||||
}
|
||||
|
||||
const gatewayId = (input: GatewayOptions) => input.gatewayId?.trim() || "default"
|
||||
|
||||
export const responsesRoute = OpenAIResponses.route.with({
|
||||
id: "cloudflare-ai-gateway-responses",
|
||||
provider: id,
|
||||
@@ -70,16 +80,12 @@ export const route = OpenAIChat.route.with({
|
||||
|
||||
export const routes = [responsesRoute, messagesRoute, route]
|
||||
|
||||
const auth = (input: LanguageModelOptions) => {
|
||||
if ("auth" in input && input.auth) return input.auth
|
||||
return Auth.optional(input.gatewayApiKey ?? ("apiKey" in input ? input.apiKey : undefined), "apiKey")
|
||||
const credential = (input: LanguageModelOptions) =>
|
||||
Auth.optional(input.gatewayApiKey ?? ("apiKey" in input ? input.apiKey : undefined), "apiKey")
|
||||
.orElse(Auth.config(authEnvVars[0]))
|
||||
.orElse(Auth.config(authEnvVars[1]))
|
||||
.bearer()
|
||||
}
|
||||
|
||||
const headers = (input: LanguageModelOptions) => ({
|
||||
...(input.gatewayId === undefined ? {} : { "cf-aig-gateway-id": input.gatewayId.trim() || "default" }),
|
||||
...(input.metadata === undefined
|
||||
? {}
|
||||
: { "cf-aig-metadata": Schema.encodeSync(Schema.fromJsonString(Schema.Unknown))(input.metadata) }),
|
||||
@@ -90,31 +96,42 @@ const headers = (input: LanguageModelOptions) => ({
|
||||
...input.headers,
|
||||
})
|
||||
|
||||
const modelID = (input: string | ModelID) => {
|
||||
const value = String(input)
|
||||
if (value.startsWith("workers-ai/")) return value.slice("workers-ai/".length)
|
||||
if (value.startsWith("anthropic/")) return `anthropic/${value.slice("anthropic/".length).replaceAll(".", "-")}`
|
||||
return value
|
||||
}
|
||||
|
||||
export const configure = (input: LanguageModelOptions) => {
|
||||
const defaults = {
|
||||
const custom = "auth" in input && input.auth ? input.auth : undefined
|
||||
const rest = {
|
||||
endpoint: { baseURL: baseURL(input) },
|
||||
auth: auth(input),
|
||||
headers: headers(input),
|
||||
auth: custom ?? credential(input).bearer(),
|
||||
headers: { "cf-aig-gateway-id": gatewayId(input), ...headers(input) },
|
||||
http: input.http,
|
||||
providerOptions: input.providerOptions,
|
||||
}
|
||||
const responses = responsesRoute.with(defaults)
|
||||
const messages = messagesRoute.with(defaults)
|
||||
const chat = route.with(defaults)
|
||||
// A custom baseURL stands in for the REST API, so every model keeps using it with gateway model IDs.
|
||||
const native = input.baseURL === undefined
|
||||
const passthrough = (provider: "anthropic/v1" | "openai") =>
|
||||
native
|
||||
? {
|
||||
...rest,
|
||||
endpoint: { baseURL: passthroughURL(input, provider) },
|
||||
auth: custom ?? Auth.bearerHeader("cf-aig-authorization", credential(input)),
|
||||
headers: headers(input),
|
||||
}
|
||||
: rest
|
||||
const responses = responsesRoute.with(passthrough("openai"))
|
||||
const messages = messagesRoute.with(passthrough("anthropic/v1"))
|
||||
const chat = route.with(rest)
|
||||
return {
|
||||
id,
|
||||
model: (input: string | ModelID) => {
|
||||
const wire = modelID(input)
|
||||
if (String(input).startsWith("openai/")) return responses.model<OpenAIProviderOptionsInput>({ id: wire })
|
||||
if (String(input).startsWith("anthropic/")) return messages.model<OpenAIProviderOptionsInput>({ id: wire })
|
||||
return chat.model<OpenAIProviderOptionsInput>({ id: wire })
|
||||
const value = String(input)
|
||||
if (value.startsWith("openai/"))
|
||||
return responses.model<OpenAIProviderOptionsInput>({ id: native ? value.slice("openai/".length) : value })
|
||||
if (value.startsWith("anthropic/"))
|
||||
return messages.model<OpenAIProviderOptionsInput>({
|
||||
id: native ? value.slice("anthropic/".length).replaceAll(".", "-") : value,
|
||||
})
|
||||
if (value.startsWith("workers-ai/"))
|
||||
return chat.model<OpenAIProviderOptionsInput>({ id: value.slice("workers-ai/".length) })
|
||||
return chat.model<OpenAIProviderOptionsInput>({ id: value })
|
||||
},
|
||||
configure,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import type { ProviderPackage } from "../provider-package.js"
|
||||
import { OpenAIChat } from "../protocols/openai-chat.js"
|
||||
import { cacheControl } from "../protocols/utils/cache.js"
|
||||
import { AuthOptions, type ProviderAuthOption } from "../route/auth-options.js"
|
||||
import { Route, type RouteDefaultsInput } from "../route/client.js"
|
||||
import { Endpoint } from "../route/endpoint.js"
|
||||
import { Protocol } from "../route/protocol.js"
|
||||
import { ProviderID, type ModelID } from "../schema/index.js"
|
||||
import type { OpenAIProviderOptionsInput } from "./openai-options.js"
|
||||
|
||||
export const id = ProviderID.make("digitalocean")
|
||||
const baseURL = "https://inference.do-ai.run/v1"
|
||||
|
||||
export type LanguageModelOptions = Omit<RouteDefaultsInput, "providerOptions"> &
|
||||
ProviderAuthOption<"optional"> & {
|
||||
readonly baseURL?: string
|
||||
readonly providerOptions?: OpenAIProviderOptionsInput
|
||||
}
|
||||
|
||||
export type Settings = ProviderPackage.Settings & OpenAIProviderOptionsInput & { readonly apiKey?: string }
|
||||
|
||||
export const protocol = Protocol.make({
|
||||
id: "digitalocean-chat",
|
||||
body: {
|
||||
schema: OpenAIChat.protocol.body.schema,
|
||||
from: (request) => OpenAIChat.fromRequest(request, { cacheControl: cacheControl() }),
|
||||
},
|
||||
stream: OpenAIChat.protocol.stream,
|
||||
})
|
||||
|
||||
export const route = Route.make({
|
||||
id: "digitalocean",
|
||||
provider: id,
|
||||
providerMetadataKey: "digitalocean",
|
||||
protocol,
|
||||
endpoint: Endpoint.path("/chat/completions", { baseURL }),
|
||||
framing: OpenAIChat.framing,
|
||||
})
|
||||
|
||||
export const routes = [route]
|
||||
|
||||
export const configure = (input: LanguageModelOptions = {}) => {
|
||||
const { apiKey: _apiKey, auth: _auth, baseURL: endpoint, ...defaults } = input
|
||||
const configured = route.with({
|
||||
...defaults,
|
||||
endpoint: { baseURL: endpoint ?? baseURL },
|
||||
auth: AuthOptions.bearer(input, ["DIGITALOCEAN_ACCESS_TOKEN", "DIGITALOCEAN_API_KEY", "DO_INFERENCE_API_KEY"]),
|
||||
})
|
||||
return {
|
||||
id,
|
||||
model: (modelID: string | ModelID) =>
|
||||
configured.model<OpenAIProviderOptionsInput>({
|
||||
id: modelID,
|
||||
compatibility: {
|
||||
supportsPromptCacheKey: true,
|
||||
},
|
||||
}),
|
||||
configure,
|
||||
}
|
||||
}
|
||||
|
||||
export const provider = configure()
|
||||
|
||||
export const model: ProviderPackage.Definition<Settings, OpenAIProviderOptionsInput>["model"] = (
|
||||
modelID,
|
||||
{ apiKey, baseURL, body, headers, ...providerOptions },
|
||||
) =>
|
||||
configure({
|
||||
apiKey,
|
||||
baseURL,
|
||||
headers,
|
||||
http: { body },
|
||||
providerOptions,
|
||||
}).model(modelID)
|
||||
|
||||
export * as DigitalOcean from "./digitalocean.js"
|
||||
@@ -37,7 +37,7 @@ export type Settings = ProviderPackage.Settings &
|
||||
const route = Route.make({
|
||||
id: "google-vertex-messages",
|
||||
provider: id,
|
||||
providerMetadataKey: "anthropic",
|
||||
providerMetadataKey: "vertex",
|
||||
protocol: Protocol.make({
|
||||
id: AnthropicMessages.protocol.id,
|
||||
body: {
|
||||
|
||||
@@ -71,7 +71,7 @@ export const protocol = Protocol.make({
|
||||
export const route = Route.make({
|
||||
id: "groq-chat",
|
||||
provider: id,
|
||||
providerMetadataKey: "openai",
|
||||
providerMetadataKey: "groq",
|
||||
protocol,
|
||||
endpoint: Endpoint.path("/chat/completions", { baseURL }),
|
||||
framing: OpenAIChat.framing,
|
||||
|
||||
@@ -14,6 +14,7 @@ export * as CloudflareWorkersAI from "./cloudflare-workers-ai.js"
|
||||
export * as DeepInfra from "./deepinfra.js"
|
||||
export * as Deepgram from "./deepgram.js"
|
||||
export * as DeepSeek from "./deepseek.js"
|
||||
export * as DigitalOcean from "./digitalocean.js"
|
||||
export * as ElevenLabs from "./elevenlabs.js"
|
||||
export * as Fal from "./fal.js"
|
||||
export * as Fireworks from "./fireworks.js"
|
||||
|
||||
@@ -3,11 +3,11 @@ import { Route, type RouteDefaultsInput } from "../route/client.js"
|
||||
import { Endpoint } from "../route/endpoint.js"
|
||||
import { Protocol } from "../route/protocol.js"
|
||||
import { AuthOptions, type ProviderAuthOption } from "../route/auth-options.js"
|
||||
import { HttpOptions, ProviderID, type CacheHint, type ModelID, type OpenString } from "../schema/index.js"
|
||||
import { HttpOptions, ProviderID, type ModelID, type OpenString } from "../schema/index.js"
|
||||
import type { ProviderPackage } from "../provider-package.js"
|
||||
import { SystemOne } from "../experimental/system-one.js"
|
||||
import { OpenAIChat } from "../protocols/openai-chat.js"
|
||||
import { newBreakpoints, ttlBucket } from "../protocols/utils/cache.js"
|
||||
import { cacheControl } from "../protocols/utils/cache.js"
|
||||
import { isRecord, ProviderShared } from "../protocols/shared.js"
|
||||
|
||||
export const id = ProviderID.make("openrouter")
|
||||
@@ -131,18 +131,6 @@ export const protocol = Protocol.make({
|
||||
stream: OpenAIChat.protocol.stream,
|
||||
})
|
||||
|
||||
const cacheControl = () => {
|
||||
const breakpoints = newBreakpoints(4)
|
||||
return (cache: CacheHint | undefined) => {
|
||||
if (cache === undefined || breakpoints.remaining === 0) return undefined
|
||||
breakpoints.remaining -= 1
|
||||
return {
|
||||
type: "ephemeral" as const,
|
||||
...(ttlBucket(cache.ttlSeconds) === "1h" ? { ttl: "1h" } : {}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OpenRouter forwards `reasoning.max_tokens` as the upstream thinking budget. Upstreams such as Anthropic and Alibaba
|
||||
// reject one that is not below the output limit; 1,024 is Anthropic's minimum budget.
|
||||
const fitReasoning = (reasoning: Record<string, unknown>, maxTokens: number | undefined) =>
|
||||
|
||||
@@ -159,6 +159,15 @@ const nativeTransportFailure = (error: unknown) => {
|
||||
return failure
|
||||
}
|
||||
|
||||
// HTTP hooks re-wrap response bodies, so a read failure can arrive as an HttpClientError caused by
|
||||
// another HttpClientError. The innermost cause carries the native failure.
|
||||
const rootCause = (error: unknown): unknown =>
|
||||
HttpClientError.isHttpClientError(error) && "cause" in error.reason && error.reason.cause !== undefined
|
||||
? rootCause(error.reason.cause)
|
||||
: error
|
||||
|
||||
const CONNECTION_LOST = "Connection lost while reading the response"
|
||||
|
||||
const httpError = (input: {
|
||||
readonly error: unknown
|
||||
readonly request: HttpClientRequest.HttpClientRequest
|
||||
@@ -179,20 +188,21 @@ const httpError = (input: {
|
||||
}),
|
||||
})
|
||||
|
||||
const source =
|
||||
HttpClientError.isHttpClientError(input.error) && "cause" in input.error.reason
|
||||
? (input.error.reason.cause ?? input.error)
|
||||
: input.error
|
||||
const source = rootCause(input.error)
|
||||
const native = nativeTransportFailure(source)
|
||||
const code = native?.code
|
||||
const raw = native?.message ?? (input.error instanceof Error ? input.error.message : undefined)
|
||||
const detail = raw
|
||||
const message = code && detail && !detail.includes(code) ? `${code}: ${detail}` : detail
|
||||
const detail =
|
||||
code && native?.message && !native.message.includes(code) ? `${code}: ${native.message}` : native?.message
|
||||
const message = detail ?? (input.error instanceof Error ? input.error.message : undefined)
|
||||
|
||||
if (Cause.isTimeoutError(input.error) || Cause.isTimeoutError(source))
|
||||
return transportError({ message: message ?? "HTTP transport timed out", code: code ?? "Timeout" })
|
||||
if (!HttpClientError.isHttpClientError(input.error))
|
||||
return transportError({ message: message ?? "HTTP transport failed", code })
|
||||
// Effect reports every response body read failure as a DecodeError, but the raw byte stream decodes
|
||||
// nothing: provider output parsing happens later and fails as InvalidProviderOutput.
|
||||
if (input.operation === "read" && input.error.reason._tag === "DecodeError")
|
||||
return transportError({ message: detail ? `${CONNECTION_LOST}: ${detail}` : CONNECTION_LOST, code })
|
||||
if (input.error.reason._tag === "TransportError") {
|
||||
return transportError({
|
||||
message: message ?? input.error.reason.description ?? "HTTP transport failed",
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import { Effect } from "effect"
|
||||
import { Clock, Duration, Effect, Stream } from "effect"
|
||||
import { Headers, HttpClientRequest } from "effect/unstable/http"
|
||||
import { Auth } from "../auth.js"
|
||||
import { render as renderEndpoint } from "../endpoint.js"
|
||||
import { Framing } from "../framing.js"
|
||||
import type { HttpMiddleware, Transport, TransportPrepareInput } from "./index.js"
|
||||
import * as ProviderShared from "../../protocols/shared.js"
|
||||
import { mergeJsonRecords, type LLMRequest } from "../../schema/index.js"
|
||||
import {
|
||||
AIError,
|
||||
DEFAULT_HTTP_TIMEOUT_MS,
|
||||
mergeJsonRecords,
|
||||
TransportError,
|
||||
type HttpContext,
|
||||
type HttpTimeout,
|
||||
type LLMRequest,
|
||||
} from "../../schema/index.js"
|
||||
import { RequestExecutor } from "../executor.js"
|
||||
|
||||
export type JsonRequestInput<Body> = TransportPrepareInput<Body>
|
||||
@@ -87,17 +95,53 @@ export const httpJson = <Body, Frame>(input: HttpJsonInput<Body, Frame>): HttpJs
|
||||
middleware: prepareInput.middleware,
|
||||
}
|
||||
}),
|
||||
execute: (prepared, _request, runtime) =>
|
||||
execute: (prepared, request, runtime) =>
|
||||
Effect.gen(function* () {
|
||||
const response = yield* runtime.http.execute(prepared.request, prepared.middleware)
|
||||
const timeout = (operation: "request" | "read", message: string, http?: HttpContext) =>
|
||||
new AIError({
|
||||
reason: new TransportError({
|
||||
message,
|
||||
transport: "http",
|
||||
operation,
|
||||
code: "Timeout",
|
||||
url: prepared.request.url,
|
||||
http,
|
||||
}),
|
||||
})
|
||||
const started = yield* Clock.currentTimeMillis
|
||||
// Unlike the header and chunk limits, the whole-request budget has no default.
|
||||
const total = request.http?.timeout ? Duration.millis(request.http.timeout) : Duration.infinity
|
||||
const response = yield* runtime.http.execute(prepared.request, prepared.middleware).pipe(
|
||||
Effect.timeoutOrElse({
|
||||
duration: Duration.min(timeoutDuration(request.http?.headerTimeout), total),
|
||||
orElse: () => timeout("request", "Timed out waiting for response headers"),
|
||||
}),
|
||||
)
|
||||
const http = RequestExecutor.responseHttp(response)
|
||||
const remaining = Duration.subtract(total, Duration.millis((yield* Clock.currentTimeMillis) - started))
|
||||
return {
|
||||
frames: prepared.framing.frame(RequestExecutor.responseStream(response)),
|
||||
http: RequestExecutor.responseHttp(response),
|
||||
frames: prepared.framing.frame(
|
||||
RequestExecutor.responseStream(response).pipe(
|
||||
Stream.timeoutOrElse({
|
||||
duration: timeoutDuration(request.http?.chunkTimeout),
|
||||
orElse: () => Stream.fail(timeout("read", "Timed out waiting for response data", http)),
|
||||
}),
|
||||
Stream.interruptWhen(
|
||||
Effect.sleep(remaining).pipe(
|
||||
Effect.andThen(Effect.fail(timeout("read", "Timed out waiting for the response to complete", http))),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
http,
|
||||
body: prepared.framing.body,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
const timeoutDuration = (value: HttpTimeout | undefined) =>
|
||||
value === false ? Duration.infinity : Duration.millis(value ?? DEFAULT_HTTP_TIMEOUT_MS)
|
||||
|
||||
export const sseJson = {
|
||||
id: "http-json/sse",
|
||||
with: <Body>() => httpJson<Body, string>({ framing: Framing.sse }),
|
||||
|
||||
@@ -264,6 +264,10 @@ export type ToolError = Schema.Schema.Type<typeof ToolError>
|
||||
export const FinishReasonDetails = Schema.Struct({
|
||||
normalized: FinishReason,
|
||||
raw: Schema.optional(Schema.String),
|
||||
/** The provider's policy area for a content-filter finish, such as `cyber`. */
|
||||
category: Schema.optional(Schema.String),
|
||||
/** The provider's human-readable reason for a content-filter finish. */
|
||||
explanation: Schema.optional(Schema.String),
|
||||
}).annotate({ identifier: "LLM.FinishReasonDetails" })
|
||||
export type FinishReasonDetails = Schema.Schema.Type<typeof FinishReasonDetails>
|
||||
|
||||
|
||||
@@ -48,10 +48,23 @@ export const mergeProviderOptions = (
|
||||
...items: ReadonlyArray<ProviderOptions | undefined>
|
||||
): ProviderOptions | undefined => mergeJsonRecords(...items)
|
||||
|
||||
/** Milliseconds for an HTTP timeout, or `false` to disable it. */
|
||||
export const HttpTimeout = Schema.Union([Schema.Number.check(Schema.isGreaterThan(0)), Schema.Literal(false)])
|
||||
export type HttpTimeout = Schema.Schema.Type<typeof HttpTimeout>
|
||||
|
||||
/** Applied to `headerTimeout` and `chunkTimeout` when a request leaves them unset. */
|
||||
export const DEFAULT_HTTP_TIMEOUT_MS = 300_000
|
||||
|
||||
export class HttpOptions extends Schema.Class<HttpOptions>("AI.HttpOptions")({
|
||||
body: Schema.optional(JsonSchema),
|
||||
headers: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
||||
query: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
||||
/** Time allowed for the whole request, from send until the response completes. Unbounded when unset. */
|
||||
timeout: Schema.optional(HttpTimeout),
|
||||
/** Time allowed for response headers to arrive. */
|
||||
headerTimeout: Schema.optional(HttpTimeout),
|
||||
/** Time allowed between streamed response chunks once headers have arrived. */
|
||||
chunkTimeout: Schema.optional(HttpTimeout),
|
||||
}) {}
|
||||
|
||||
export namespace HttpOptions {
|
||||
@@ -70,8 +83,12 @@ export const mergeHttpOptions = (...items: ReadonlyArray<HttpOptions | undefined
|
||||
const body = mergeJsonRecords(...items.map((item) => item?.body))
|
||||
const headers = mergeStringRecords(...items.map((item) => item?.headers))
|
||||
const query = mergeStringRecords(...items.map((item) => item?.query))
|
||||
if (!body && !headers && !query) return undefined
|
||||
return new HttpOptions({ body, headers, query })
|
||||
const timeout = items.findLast((item) => item?.timeout !== undefined)?.timeout
|
||||
const headerTimeout = items.findLast((item) => item?.headerTimeout !== undefined)?.headerTimeout
|
||||
const chunkTimeout = items.findLast((item) => item?.chunkTimeout !== undefined)?.chunkTimeout
|
||||
if (!body && !headers && !query && timeout === undefined && headerTimeout === undefined && chunkTimeout === undefined)
|
||||
return undefined
|
||||
return new HttpOptions({ body, headers, query, timeout, headerTimeout, chunkTimeout })
|
||||
}
|
||||
|
||||
export class GenerationOptions extends Schema.Class<GenerationOptions>("LLM.GenerationOptions")({
|
||||
|
||||
@@ -6,10 +6,17 @@ const MISSING_TOOL_RESULT = "Tool result missing"
|
||||
export function normalizeToolHistory(messages: ReadonlyArray<Message>) {
|
||||
const normalized: Message[] = []
|
||||
const pending = new Map<string, ToolCallPart>()
|
||||
// System updates cannot sit between a tool call and its results, so they wait until every pending call is answered.
|
||||
const held: Message[] = []
|
||||
const releaseHeld = () => {
|
||||
if (pending.size > 0) return
|
||||
normalized.push(...held)
|
||||
held.length = 0
|
||||
}
|
||||
const appendMissingResults = () => {
|
||||
if (pending.size === 0) return
|
||||
normalized.push(missingToolResults(pending.values()))
|
||||
if (pending.size > 0) normalized.push(missingToolResults(pending.values()))
|
||||
pending.clear()
|
||||
releaseHeld()
|
||||
}
|
||||
|
||||
for (const message of messages) {
|
||||
@@ -18,6 +25,12 @@ export function normalizeToolHistory(messages: ReadonlyArray<Message>) {
|
||||
if (message.role === "tool") {
|
||||
const tool = normalizeToolMessage(message, pending)
|
||||
if (tool) normalized.push(tool)
|
||||
releaseHeld()
|
||||
continue
|
||||
}
|
||||
|
||||
if (message.role === "system" && pending.size > 0) {
|
||||
held.push(message)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -27,6 +40,7 @@ export function normalizeToolHistory(messages: ReadonlyArray<Message>) {
|
||||
if (part.type === "tool-call" && part.providerExecuted !== true) pending.set(part.id, part)
|
||||
}
|
||||
}
|
||||
appendMissingResults()
|
||||
|
||||
return normalized.length === messages.length && normalized.every((message, index) => message === messages[index])
|
||||
? messages
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
AmazonBedrock,
|
||||
AnthropicCompatible,
|
||||
CloudflareAIGateway,
|
||||
DigitalOcean,
|
||||
GoogleVertexMessages,
|
||||
Meta,
|
||||
MiniMax,
|
||||
@@ -117,6 +118,86 @@ describe("applyCachePolicy", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("'auto' emits cache_control markers on DigitalOcean", () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({
|
||||
model: DigitalOcean.configure({ apiKey: "test" }).model("anthropic-claude-fable-5.1"),
|
||||
system: "You are concise.",
|
||||
tools: [{ name: "lookup", description: "Look up a value", inputSchema: { type: "object", properties: {} } }],
|
||||
prompt: "hi",
|
||||
}),
|
||||
)
|
||||
|
||||
expect(prepared.body).toMatchObject({
|
||||
tools: [{ type: "function", function: { name: "lookup" }, cache_control: { type: "ephemeral" } }],
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: [{ text: "You are concise.", cache_control: { type: "ephemeral" } }],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: [{ text: "hi", cache_control: { type: "ephemeral" } }],
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("Alibaba chat caches the system and conversation tail without marking tools", () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({
|
||||
model: Alibaba.configure({ region: "ap-southeast-1", apiKey: "test" }).chat("qwen3.8-max"),
|
||||
system: "You are concise.",
|
||||
tools: [{ name: "lookup", description: "Look up a value", inputSchema: { type: "object", properties: {} } }],
|
||||
prompt: "hi",
|
||||
}),
|
||||
)
|
||||
|
||||
expect(prepared.body).toMatchObject({
|
||||
tools: [{ type: "function", function: { name: "lookup" } }],
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: [{ text: "You are concise.", cache_control: { type: "ephemeral" } }],
|
||||
},
|
||||
{ role: "user", content: [{ text: "hi", cache_control: { type: "ephemeral" } }] },
|
||||
],
|
||||
})
|
||||
expect(prepared.body.tools?.[0]?.cache_control).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("Alibaba chat omits automatic cache markers when cache is none", () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({
|
||||
model: Alibaba.configure({ region: "ap-southeast-1", apiKey: "test" }).chat("qwen3.8-max"),
|
||||
system: "You are concise.",
|
||||
prompt: "hi",
|
||||
cache: "none",
|
||||
}),
|
||||
)
|
||||
|
||||
expect(JSON.stringify(prepared.body)).not.toContain("cache_control")
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("Alibaba chat does not assume non-Qwen models support cache markers", () =>
|
||||
Effect.gen(function* () {
|
||||
const alibaba = Alibaba.configure({ region: "ap-southeast-1", apiKey: "test" })
|
||||
for (const modelID of ["kimi-k3", "glm-5.2", "deepseek-v4-flash-0731", "MiniMax-M2.5"]) {
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({ model: alibaba.chat(modelID), system: "You are concise.", prompt: "hi" }),
|
||||
)
|
||||
|
||||
expect(JSON.stringify(prepared.body)).not.toContain("cache_control")
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("'auto' emits Anthropic cache markers on Anthropic-compatible routes", () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* compileRequest(
|
||||
|
||||
@@ -147,7 +147,7 @@ describe("Anthropic Messages effort updates", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("accepts a marker between a tool call and its result", () =>
|
||||
it.effect("moves a marker between a tool call and its result after the result", () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({
|
||||
@@ -166,8 +166,8 @@ describe("Anthropic Messages effort updates", () => {
|
||||
expect(prepared.body.messages).toEqual([
|
||||
{ role: "user", content: [{ type: "text", text: "Weather?" }] },
|
||||
{ role: "assistant", content: [{ type: "tool_use", id: "call_1", name: "lookup", input: {} }] },
|
||||
{ role: "system", content: [], output_config: { effort: "low" } },
|
||||
{ role: "user", content: [{ type: "tool_result", tool_use_id: "call_1", content: '{"temp":72}' }] },
|
||||
{ role: "system", content: [], output_config: { effort: "low" } },
|
||||
])
|
||||
}),
|
||||
)
|
||||
@@ -197,9 +197,15 @@ describe("Anthropic Messages effort updates", () => {
|
||||
["anthropic/claude-opus-5", true],
|
||||
["claude-fable-5-1", true],
|
||||
["claude-mythos-5-1", true],
|
||||
["claude-opus-5-5", true],
|
||||
["claude-sonnet-5-5", true],
|
||||
["anthropic/claude-sonnet-5-5", true],
|
||||
["claude-sonnet-6", true],
|
||||
["claude-haiku-6", true],
|
||||
["claude-fable-5", false],
|
||||
["claude-opus-4-8", false],
|
||||
["claude-sonnet-5", false],
|
||||
["claude-sonnet-5-20260801", false],
|
||||
["kimi-k2.5", false],
|
||||
] as const) {
|
||||
it.effect(`${supported ? "lowers" : "strips"} markers for ${id}`, () =>
|
||||
@@ -375,11 +381,24 @@ describe("OpenAI Responses effort updates", () => {
|
||||
["openai/gpt-6-sol", true],
|
||||
["gpt-6-luna", true],
|
||||
["openai/gpt-6-luna", true],
|
||||
["gpt-6-astra-2026-09-01", false],
|
||||
["gpt-6-sol-pro", false],
|
||||
["gpt-6-luna-pro", false],
|
||||
["gpt-6-sol-fast", false],
|
||||
["gpt-6", true],
|
||||
["gpt-6.1-sol", true],
|
||||
["openai/gpt-6.1-sol", true],
|
||||
["GPT-6.1-SOL", true],
|
||||
["gpt-6-astra-2026-09-01", true],
|
||||
["gpt-6-sol-pro", true],
|
||||
["gpt-6-luna-pro", true],
|
||||
["gpt-6-sol-fast", true],
|
||||
["gpt-7", true],
|
||||
["openai/gpt-7.2-new-family", true],
|
||||
["gpt-10.1", true],
|
||||
["gpt-5", false],
|
||||
["gpt-5.6-sol", false],
|
||||
["gpt-5.10", false],
|
||||
["not-gpt-6-sol", false],
|
||||
["gpt-6foo", false],
|
||||
["gpt-6.x-sol", false],
|
||||
["future-model", false],
|
||||
] as const) {
|
||||
it.effect(`${supported ? "lowers" : "strips"} markers for ${id}`, () =>
|
||||
Effect.gen(function* () {
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import { describe, expect } from "bun:test"
|
||||
import { Deferred, Effect, Fiber, Layer, Ref, Stream } from "effect"
|
||||
import { Headers, HttpClientError, HttpClientRequest } from "effect/unstable/http"
|
||||
import { LLM, AIError, HttpContext, InvalidProviderOutputError, TransportError } from "../src/index.js"
|
||||
import { LLMClient, RequestExecutor, WebSocketTransport, type WebSocketChannelExecutor } from "../src/route.js"
|
||||
import { Headers, HttpClientError, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
|
||||
import { LLM, AIError, HttpContext, InvalidProviderOutputError, TransportError, isRetryable } from "../src/index.js"
|
||||
import {
|
||||
LLMClient,
|
||||
RequestExecutor,
|
||||
WebSocketTransport,
|
||||
type HttpMiddleware,
|
||||
type WebSocketChannelExecutor,
|
||||
} from "../src/route.js"
|
||||
import { route } from "../src/protocols/openai-chat.js"
|
||||
import { configure } from "../src/providers/openai.js"
|
||||
import { dynamicResponse, fixedResponse, handlerLayer, systemError } from "./lib/http.js"
|
||||
import { dynamicResponse, fixedResponse, handlerLayer, systemError, truncatedStream } from "./lib/http.js"
|
||||
import { deltaChunk } from "./lib/openai-chunks.js"
|
||||
import { sseEvents, sseRaw } from "./lib/sse.js"
|
||||
import { it } from "./lib/effect.js"
|
||||
@@ -18,6 +24,8 @@ const secretRequest = HttpClientRequest.post("https://provider.test/v1/chat?api_
|
||||
HttpClientRequest.setHeaders(Headers.fromInput({ authorization: "Bearer header-secret-456" })),
|
||||
)
|
||||
|
||||
const sseRequest = HttpClientRequest.post("https://provider.test/v1/messages")
|
||||
|
||||
const expectAIError = (error: unknown) => {
|
||||
expect(error).toBeInstanceOf(AIError)
|
||||
if (!(error instanceof AIError)) throw new Error("expected AIError")
|
||||
@@ -93,7 +101,9 @@ describe("RequestExecutor", () => {
|
||||
const error = yield* RequestExecutor.stream(executor, secretRequest).pipe(Stream.runDrain, Effect.flip)
|
||||
|
||||
expectAIError(error)
|
||||
expect(error.message).toBe("ECONNRESET: disconnected query-secret-123 header-secret-456")
|
||||
expect(error.message).toBe(
|
||||
"Connection lost while reading the response: ECONNRESET: disconnected query-secret-123 header-secret-456",
|
||||
)
|
||||
expect(error.reason.http).toMatchObject({ status: 200, url: secretRequest.url })
|
||||
expect(error.reason.cause).toMatchObject({ code: "ECONNRESET" })
|
||||
expect(error.reason).toMatchObject({
|
||||
@@ -123,7 +133,7 @@ describe("RequestExecutor", () => {
|
||||
const error = yield* RequestExecutor.stream(executor, secretRequest).pipe(Stream.runDrain, Effect.flip)
|
||||
|
||||
expectAIError(error)
|
||||
expect(error.message).toBe("ECONNRESET: socket closed")
|
||||
expect(error.message).toBe("Connection lost while reading the response: ECONNRESET: socket closed")
|
||||
expect(error.reason.cause).toBeInstanceOf(TypeError)
|
||||
expect(error.reason).toMatchObject({
|
||||
_tag: "Transport",
|
||||
@@ -144,6 +154,54 @@ describe("RequestExecutor", () => {
|
||||
),
|
||||
)
|
||||
|
||||
it.effect("reports a connection lost mid-stream through middleware that re-wraps the body", () =>
|
||||
Effect.gen(function* () {
|
||||
const executor = yield* RequestExecutor.Service
|
||||
const chunks: Array<Uint8Array> = []
|
||||
// Session HTTP hooks hand plugins a web Response, so the body is re-wrapped around the original stream.
|
||||
const rewrap: HttpMiddleware = (input, handler) =>
|
||||
Effect.gen(function* () {
|
||||
const response = yield* handler(input)
|
||||
const body = yield* Stream.toReadableStreamEffect(response.stream)
|
||||
return HttpClientResponse.fromWeb(
|
||||
input,
|
||||
new Response(body, { status: response.status, headers: response.headers }),
|
||||
)
|
||||
})
|
||||
const error = yield* RequestExecutor.stream(executor, sseRequest, rewrap).pipe(
|
||||
Stream.runForEach((chunk) => Effect.sync(() => chunks.push(chunk))),
|
||||
Effect.flip,
|
||||
)
|
||||
|
||||
expectAIError(error)
|
||||
expect(new TextDecoder().decode(chunks[0])).toBe('data: {"type":"ping"}\n\n')
|
||||
expect(error.message).toBe("Connection lost while reading the response: ECONNRESET: other side closed")
|
||||
expect(error.reason.cause).toBeInstanceOf(TypeError)
|
||||
expect(error.reason.http).toMatchObject({ status: 200 })
|
||||
expect(error.reason).toMatchObject({ _tag: "Transport", operation: "read", code: "ECONNRESET" })
|
||||
expect(isRetryable(error)).toBeTrue()
|
||||
}).pipe(
|
||||
Effect.provide(
|
||||
truncatedStream(
|
||||
['data: {"type":"ping"}\n\n'],
|
||||
new TypeError("terminated", { cause: systemError("ECONNRESET", "other side closed") }),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
it.effect("does not report a body read failure without a native cause as a decode error", () =>
|
||||
Effect.gen(function* () {
|
||||
const executor = yield* RequestExecutor.Service
|
||||
const error = yield* RequestExecutor.stream(executor, sseRequest).pipe(Stream.runDrain, Effect.flip)
|
||||
|
||||
expectAIError(error)
|
||||
expect(error.message).toBe("Connection lost while reading the response")
|
||||
expect(error.reason).toMatchObject({ _tag: "Transport", operation: "read", code: undefined })
|
||||
expect(isRetryable(error)).toBeTrue()
|
||||
}).pipe(Effect.provide(truncatedStream(['data: {"type":"ping"}\n\n'], new DOMException("aborted", "AbortError")))),
|
||||
)
|
||||
|
||||
it.effect("preserves middleware error messages", () =>
|
||||
Effect.gen(function* () {
|
||||
const executor = yield* RequestExecutor.Service
|
||||
|
||||
Vendored
+3
-3
@@ -10,7 +10,7 @@
|
||||
"usage"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-7-plus-streams-thinking-disabled",
|
||||
"recordedAt": "2026-09-08T03:10:42.782Z"
|
||||
"recordedAt": "2026-10-02T01:23:37.836Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -21,14 +21,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.7-plus\",\"messages\":[{\"role\":\"user\",\"content\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"max_completion_tokens\":4096,\"enable_thinking\":false}"
|
||||
"body": "{\"model\":\"qwen3.7-plus\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"max_completion_tokens\":4096,\"enable_thinking\":false}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-047bcb67-b193-9a4f-9d77-ea0325be6d7c\",\"created\":1788837041,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-047bcb67-b193-9a4f-9d77-ea0325be6d7c\",\"choices\":[{\"delta\":{\"content\":\"3\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837041,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-047bcb67-b193-9a4f-9d77-ea0325be6d7c\",\"choices\":[{\"delta\":{\"content\":\"7887\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837041,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-047bcb67-b193-9a4f-9d77-ea0325be6d7c\",\"choices\":[{\"delta\":{\"content\":\"\"},\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null}],\"created\":1788837041,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"choices\":[],\"created\":1788837041,\"id\":\"chatcmpl-047bcb67-b193-9a4f-9d77-ea0325be6d7c\",\"model\":\"qwen3.7-plus\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":5,\"prompt_tokens\":32,\"prompt_tokens_details\":{\"cached_tokens\":0,\"text_tokens\":32},\"total_tokens\":37}}\n\ndata: [DONE]\n\n"
|
||||
"body": "data: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-e96a3356-bec9-98ad-bdbe-af00a15ee965\",\"created\":1790904216,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-e96a3356-bec9-98ad-bdbe-af00a15ee965\",\"choices\":[{\"delta\":{\"content\":\"3\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904216,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-e96a3356-bec9-98ad-bdbe-af00a15ee965\",\"choices\":[{\"delta\":{\"content\":\"7887\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904216,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-e96a3356-bec9-98ad-bdbe-af00a15ee965\",\"choices\":[{\"delta\":{\"content\":\"\"},\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null}],\"created\":1790904216,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"choices\":[],\"created\":1790904216,\"id\":\"chatcmpl-e96a3356-bec9-98ad-bdbe-af00a15ee965\",\"model\":\"qwen3.7-plus\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":5,\"prompt_tokens\":32,\"prompt_tokens_details\":{\"cache_creation\":{\"ephemeral_5m_input_tokens\":0},\"cache_creation_input_tokens\":0,\"cache_type\":\"ephemeral\",\"cache_write_tokens\":0,\"cached_tokens\":0,\"text_tokens\":32},\"total_tokens\":37}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Vendored
+3
-3
@@ -10,7 +10,7 @@
|
||||
"usage"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-7-plus-streams-thinking-enabled",
|
||||
"recordedAt": "2026-09-08T03:10:54.099Z"
|
||||
"recordedAt": "2026-10-02T01:23:43.998Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -21,14 +21,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.7-plus\",\"messages\":[{\"role\":\"user\",\"content\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"max_completion_tokens\":4096,\"enable_thinking\":true,\"thinking_budget\":1024}"
|
||||
"body": "{\"model\":\"qwen3.7-plus\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"max_completion_tokens\":4096,\"enable_thinking\":true,\"thinking_budget\":1024}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"Thinking\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" Process:\\n\\n1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\". **Ident\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"ify the core question\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\":** The user wants\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" to know the product\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" of \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"173 and\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 219\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\".\\n2.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" **Identify\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" the constraint:** The\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" response must contain *\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"only\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-ec928416-c99b-9f46-9805-cbb37bd7c499\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"* the final integer\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837042,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"cLine truncated
|
||||
"body": "data: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"Thinking\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" Process:\\n\\n1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\". Identify\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" the core\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" question: Calculate \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"173 *\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 219\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\".\\n2.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" Constraint: Reply\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" with *\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"only* the final\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" integer.\\n3\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\". Perform the\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" multiplication:\\n\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fe-9ee8-a442-7a3881697fdf\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" * \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904218,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.7-plus\",\"id\":\"chatcmpl-70ed9ebc-62fLine truncated
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+10
-4
@@ -1,9 +1,15 @@
|
||||
{
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"tags": ["prefix:alibaba-chat", "provider:alibaba", "protocol:alibaba-chat", "region:ap-southeast-1", "image"],
|
||||
"tags": [
|
||||
"prefix:alibaba-chat",
|
||||
"provider:alibaba",
|
||||
"protocol:alibaba-chat",
|
||||
"region:ap-southeast-1",
|
||||
"image"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-8-flash-reads-image-bytes",
|
||||
"recordedAt": "2026-09-08T03:10:55.585Z"
|
||||
"recordedAt": "2026-10-02T01:23:45.179Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -14,14 +20,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-flash\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Read the three words in this image. Reply with only the words in order.\"},{\"type\":\"image_url\",\"image_url\":{\"url\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAnYAAACKCAYAAAAnmweyAAACKWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjYzMCIKICAgZXhpZjpVc2VyQ29tbWVudD0iU2NyZWVuc2hvdCIKICAgZXhpZjpQaXhlbFlEaW1lbnNpb249IjEzOCIKICAgdGlmZjpZUmVzb2x1dGlvbj0iMTQ0LzEiCiAgIHRpZmY6WFJlc29sdXRpb249IjE0NC8xIgogICB0aWZmOlJlc29sdXRpb25Vbml0PSIyIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+at0SpgAACrhpQ0NQSUNDIFByb2ZpbGUAAEiJlZcHUFNZF8fvey+dhJYQASmh994CSAmhBVCQDjZCEiAQQkxBwa4sruBaUBHBsqKrIgo2qg0RxbYo9r4gi4iyLhZsqHwPGMLufvN933xn5s75zXnn/u+5d959cx4AFFOuRCKC1QHIFsul0SEBjMSkZAb+JcACTUACnoDK5ckkrKioCIDahP+7fbgLoFF/y25U69+f/1fT4AtkPACgKJRT+TJeNsonAIABTyKVA4CgDEwWyCWjfB9lmhQtEOWBUU4fY8yoDi11nGljObHRbJQtASCQuVxpOgBkVzTOyOWlozrkWJQdxXyhGOUClH2zs3P4KLehbInmSFAe1Wem/kUn/W+aqUpNLjddyeN7GTNCoFAmEXHz/s/j+N+WLVJMrGGBDnKGNDQa9Xrouf2elROuZHHqjMgJFvLH8sc4QxEaN8E8GTt5gmWiGM4E87mB4Uod0YyICU4TBitzhHJO7AQLZEExEyzNiVaumyZlsyaYK52sQZEVp4xnCDhK/fyM2IQJzhXGz1DWlhUTPpnDVsalimjlXgTikIDJdYOV55At+8vehRzlXHlGbKjyHLiT9QvErElNWaKyNr4gMGgyJ06ZL5EHKNeSiKKU+QJRiDIuy41RzpWjL+fk3CjlGWZyw6ImGMQAOVAAPhCCHMAAgaiXAQkQAS7IkwsWykc3xM6R5EmF6RlyBgu9dQIGR8yzt2U4Ozq7AzB6h8dfkXf0sbsJ0a9MxlZVAeDTNDIycnIyFnYDgKMpAJDqJmOWcwBQ7wPg0imeQpo7Hhu7a1j0y6AGaEAHGAATYAnsgDNwB97AHwSBMBAJYkESmAt4IANkAylYABaDFaAQFIMNYAsoB7vAHnAAHAbHQAM4Bc6Bi+AquAHugEegC/SCV2AQfADDEAThIQpEhXQgQ8gMsoGcISbkCwVBEVA0lASlQOmQGFJAi6FVUDFUApVDu6Eq6CjUBJ2DLkOd0AOoG+qH3kJfYAQmwzRYHzaHHWAmzILD4Vh4DpwOz4fz4QJ4HVwGV8KH4Hr4HHwVvgN3wa/gIQQgKggdMULsECbCRiKRZCQNkSJLkSKkFKlEapBmpB25hXQhA8hnDA5DxTAwdhhvTCgmDsPDzMcsxazFlGMOYOoxbZhbmG7MIOY7loLVw9pgvbAcbCI2HbsAW4gtxe7D1mEvYO9ge7EfcDgcHWeB88CF4pJwmbhFuLW4HbhaXAuuE9eDG8Lj8Tp4G7wPPhLPxcvxhfht+EP4s/ib+F78J4IKwZDgTAgmJBPEhJWEUsJBwhnCTUIfYZioTjQjehEjiXxiHnE9cS+xmXid2EscJmmQLEg+pFhSJmkFqYxUQ7pAekx6p6KiYqziqTJTRaiyXKVM5YjKJZVulc9kTbI1mU2eTVaQ15H3k1vID8jvKBSKOcWfkkyRU9ZRqijnKU8pn1SpqvaqHFW+6jLVCtV61Zuqr9WIamZqLLW5avlqpWrH1a6rDagT1c3V2epc9aXqFepN6vfUhzSoGk4akRrZGms1Dmpc1nihidc01wzS5GsWaO7RPK/ZQ0WoJlQ2lUddRd1LvUDtpeFoFjQOLZNWTDtM66ANamlquWrFay3UqtA6rdVFR+jmdA5dRF9PP0a/S/8yRX8Ka4pgypopNVNuTvmoPVXbX1ugXaRdq31H+4sOQydIJ0tno06DzhNdjK617kzdBbo7dS/oDkylTfWeyptaNPXY1Id6sJ61XrTeIr09etf0hvQN9EP0Jfrb9M/rDxjQDfwNMg02G5wx6DekGvoaCg03G541fMnQYrAYIkYZo40xaKRnFGqkMNpt1GE0bGxhHGe80rjW+IkJyYRpkmay2aTVZNDU0HS66WLTatOHZkQzplmG2VazdrOP5hbmCearzRvMX1hoW3As8i2qLR5bUiz9LOdbVlretsJZMa2yrHZY3bCGrd2sM6wrrK/bwDbuNkKbHTadtlhbT1uxbaXtPTuyHcsu167artuebh9hv9K+wf61g6lDssNGh3aH745ujiLHvY6PnDSdwpxWOjU7vXW2duY5VzjfdqG4BLssc2l0eeNq4ypw3el6343qNt1ttVur2zd3D3epe417v4epR4rHdo97TBozirmWeckT6xnguczzlOdnL3cvudcxrz+97byzvA96v5hmMU0wbe+0Hh9jH67Pbp8uX4Zviu/Pvl1+Rn5cv0q/Z/4m/nz/ff59LCtWJusQ63WAY4A0oC7gI9uLvYTdEogEhgQWBXYEaQbFBZUHPQ02Dk4Prg4eDHELWRTSEooNDQ/dGHqPo8/hcao4g2EeYUvC2sLJ4THh5eHPIqwjpBHN0+HpYdM3TX88w2yGeEZDJIjkRG6KfBJlETU/6uRM3MyomRUzn0c7RS+Obo+hxsyLORjzITYgdn3sozjLOEVca7xa/Oz4qviPCYEJJQldiQ6JSxKvJukmCZMak/HJ8cn7kodmBc3aMqt3ttvswtl351jMWTjn8lzduaK5p+epzePOO56CTUlIOZjylRvJreQOpXJSt6cO8ti8rbxXfH/+Zn6/wEdQIuhL80krSXuR7pO+Kb0/wy+jNGNAyBaWC99khmbuyvyYFZm1P2tElCCqzSZkp2Q3iTXFWeK2HIOchTmdEhtJoaRrvtf8LfMHpeHSfTJINkfWKKehzdI1haXiB0V3rm9uRe6nBfELji/UWCheeC3POm9NXl9+cP4vizCLeItaFxstXrG4ewlrye6l0NLUpa3LTJYVLOtdHrL8wArSiqwVv650XFmy8v2qhFXNBfoFywt6fgj5obpQtVBaeG+19+pdP2J+FP7YscZlzbY134v4RVeKHYtLi7+u5a298pPTT2U/jaxLW9ex3n39zg24DeINdzf6bTxQolGSX9Kzafqm+s2MzUWb32+Zt+VyqWvprq2krYqtXWURZY3bTLdt2Pa1PKP8TkVARe12ve1rtn/cwd9xc6f/zppd+ruKd335Wfjz/d0hu+srzStL9+D25O55vjd+b/svzF+q9unuK973bb94f9eB6ANtVR5VVQf1Dq6vhqsV1f2HZh+6cTjwcGONXc3uWnpt8RFwRHHk5dGUo3ePhR9rPc48XnPC7MT2OmpdUT1Un1c/2JDR0NWY1NjZFNbU2uzdXHfS/uT+U0anKk5rnV5/hnSm4MzI2fyzQy2SloFz6ed6Wue1PjqfeP5228y2jgvhFy5dDL54vp3VfvaSz6VTl70uN11hXmm46n61/prbtbpf3X6t63DvqL/ucb3xhueN5s5pnWdu+t08dyvw1sXbnNtX78y403k37u79e7Pvdd3n33/xQPTgzcPch8OPlj/GPi56ov6k9Kne08rfrH6r7XLvOt0d2H3tWcyzRz28nle/y37/2lvwnPK8tM+wr+qF84tT/cH9N17Oetn7SvJqeKDwD40/tr+2fH3iT/8/rw0mDva+kb4Zebv2nc67/e9d37cORQ09/ZD9Yfhj0SedTwc+Mz+3f0n40je84Cv+a9k3q2/N38O/Px7JHhmRcKXcsVYAQQeclgbA2/0AUJIAoKI9BGnWeI89ZtD4f8EYgf/E4334mKGdSw3qRtsjdgsAR9BhvhwANX8ARlujWH8Au7gox0Q/PNa7jxoO/Yup8UK0Vjk9ta0C/7Txvv4vdf/TA6Xq3/y/AOOhDyne6KAWAAAAimVYSWZNTQAqAAAACAAEARoABQAAAAEAAAA+ARsABQAAAAEAAABGASgAAwAAAAEAAgAAh2kABAAAAAEAAABOAAAAAAAAAJAAAAABAAAAkAAAAAEAA5KGAAcAAAASAAAAeKACAAQAAAABAAACdqADAAQAAAABAAAAigAAAABBU0NJSQAAAFNjAAAAAAAAAADxh4F4AAAAHGlET1QAAAACAAAAAAAAAEUAAAAoAAAARQAAAEUAAAbT33OL9AAABp9Line truncated
|
||||
"body": "{\"model\":\"qwen3.8-flash\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Read the three words in this image. Reply with only the words in order.\",\"cache_control\":{\"type\":\"ephemeral\"}},{\"type\":\"image_url\",\"image_url\":{\"url\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAnYAAACKCAYAAAAnmweyAAACKWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjYzMCIKICAgZXhpZjpVc2VyQ29tbWVudD0iU2NyZWVuc2hvdCIKICAgZXhpZjpQaXhlbFlEaW1lbnNpb249IjEzOCIKICAgdGlmZjpZUmVzb2x1dGlvbj0iMTQ0LzEiCiAgIHRpZmY6WFJlc29sdXRpb249IjE0NC8xIgogICB0aWZmOlJlc29sdXRpb25Vbml0PSIyIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+at0SpgAACrhpQ0NQSUNDIFByb2ZpbGUAAEiJlZcHUFNZF8fvey+dhJYQASmh994CSAmhBVCQDjZCEiAQQkxBwa4sruBaUBHBsqKrIgo2qg0RxbYo9r4gi4iyLhZsqHwPGMLufvN933xn5s75zXnn/u+5d959cx4AFFOuRCKC1QHIFsul0SEBjMSkZAb+JcACTUACnoDK5ckkrKioCIDahP+7fbgLoFF/y25U69+f/1fT4AtkPACgKJRT+TJeNsonAIABTyKVA4CgDEwWyCWjfB9lmhQtEOWBUU4fY8yoDi11nGljObHRbJQtASCQuVxpOgBkVzTOyOWlozrkWJQdxXyhGOUClH2zs3P4KLehbInmSFAe1Wem/kUn/W+aqUpNLjddyeN7GTNCoFAmEXHz/s/j+N+WLVJMrGGBDnKGNDQa9Xrouf2elROuZHHqjMgJFvLH8sc4QxEaN8E8GTt5gmWiGM4E87mB4Uod0YyICU4TBitzhHJO7AQLZEExEyzNiVaumyZlsyaYK52sQZEVp4xnCDhK/fyM2IQJzhXGz1DWlhUTPpnDVsalimjlXgTikIDJdYOV55At+8vehRzlXHlGbKjyHLiT9QvErElNWaKyNr4gMGgyJ06ZL5EHKNeSiKKU+QJRiDIuy41RzpWjL+fk3CjlGWZyw6ImGMQAOVAAPhCCHMAAgaiXAQkQAS7IkwsWykc3xM6R5EmF6RlyBgu9dQIGR8yzt2U4Ozq7AzB6h8dfkXf0sbsJ0a9MxlZVAeDTNDIycnIyFnYDgKMpAJDqJmOWcwBQ7wPg0imeQpo7Hhu7a1j0y6AGaEAHGAATYAnsgDNwB97AHwSBMBAJYkESmAt4IANkAylYABaDFaAQFIMNYAsoB7vAHnAAHAbHQAM4Bc6Bi+AquAHugEegC/SCV2AQfADDEAThIQpEhXQgQ8gMsoGcISbkCwVBEVA0lASlQOmQGFJAi6FVUDFUApVDu6Eq6CjUBJ2DLkOd0AOoG+qH3kJfYAQmwzRYHzaHHWAmzILD4Vh4DpwOz4fz4QJ4HVwGV8KH4Hr4HHwVvgN3wa/gIQQgKggdMULsECbCRiKRZCQNkSJLkSKkFKlEapBmpB25hXQhA8hnDA5DxTAwdhhvTCgmDsPDzMcsxazFlGMOYOoxbZhbmG7MIOY7loLVw9pgvbAcbCI2HbsAW4gtxe7D1mEvYO9ge7EfcDgcHWeB88CF4pJwmbhFuLW4HbhaXAuuE9eDG8Lj8Tp4G7wPPhLPxcvxhfht+EP4s/ib+F78J4IKwZDgTAgmJBPEhJWEUsJBwhnCTUIfYZioTjQjehEjiXxiHnE9cS+xmXid2EscJmmQLEg+pFhSJmkFqYxUQ7pAekx6p6KiYqziqTJTRaiyXKVM5YjKJZVulc9kTbI1mU2eTVaQ15H3k1vID8jvKBSKOcWfkkyRU9ZRqijnKU8pn1SpqvaqHFW+6jLVCtV61Zuqr9WIamZqLLW5avlqpWrH1a6rDagT1c3V2epc9aXqFepN6vfUhzSoGk4akRrZGms1Dmpc1nihidc01wzS5GsWaO7RPK/ZQ0WoJlQ2lUddRd1LvUDtpeFoFjQOLZNWTDtM66ANamlquWrFay3UqtA6rdVFR+jmdA5dRF9PP0a/S/8yRX8Ka4pgypopNVNuTvmoPVXbX1ugXaRdq31H+4sOQydIJ0tno06DzhNdjK617kzdBbo7dS/oDkylTfWeyptaNPXY1Id6sJ61XrTeIr09etf0hvQN9EP0Jfrb9M/rDxjQDfwNMg02G5wx6DekGvoaCg03G541fMnQYrAYIkYZo40xaKRnFGqkMNpt1GE0bGxhHGe80rjW+IkJyYRpkmay2aTVZNDU0HS66WLTatOHZkQzplmG2VazdrOP5hbmCearzRvMX1hoW3As8i2qLR5bUiz9LOdbVlretsJZMa2yrHZY3bCGrd2sM6wrrK/bwDbuNkKbHTadtlhbT1uxbaXtPTuyHcsu167artuebh9hv9K+wf61g6lDssNGh3aH745ujiLHvY6PnDSdwpxWOjU7vXW2duY5VzjfdqG4BLssc2l0eeNq4ypw3el6343qNt1ttVur2zd3D3epe417v4epR4rHdo97TBozirmWeckT6xnguczzlOdnL3cvudcxrz+97byzvA96v5hmMU0wbe+0Hh9jH67Pbp8uX4Zviu/Pvl1+Rn5cv0q/Z/4m/nz/ff59LCtWJusQ63WAY4A0oC7gI9uLvYTdEogEhgQWBXYEaQbFBZUHPQ02Dk4Prg4eDHELWRTSEooNDQ/dGHqPo8/hcao4g2EeYUvC2sLJ4THh5eHPIqwjpBHN0+HpYdM3TX88w2yGeEZDJIjkRG6KfBJlETU/6uRM3MyomRUzn0c7RS+Obo+hxsyLORjzITYgdn3sozjLOEVca7xa/Oz4qviPCYEJJQldiQ6JSxKvJukmCZMak/HJ8cn7kodmBc3aMqt3ttvswtl351jMWTjn8lzduaK5p+epzePOO56CTUlIOZjylRvJreQOpXJSt6cO8ti8rbxXfH/+Zn6/wEdQIuhL80krSXuR7pO+Kb0/wy+jNGNAyBaWC99khmbuyvyYFZm1P2tElCCqzSZkp2Q3iTXFWeK2HIOchTmdEhtJoaRrvtf8LfMHpeHSfTJINkfWKKehzdI1haXiB0V3rm9uRe6nBfELji/UWCheeC3POm9NXl9+cP4vizCLeItaFxstXrG4ewlrye6l0NLUpa3LTJYVLOtdHrL8wArSiqwVv650XFmy8v2qhFXNBfoFywt6fgj5obpQtVBaeG+19+pdP2J+FP7YscZlzbY134v4RVeKHYtLi7+u5a298pPTT2U/jaxLW9ex3n39zg24DeINdzf6bTxQolGSX9Kzafqm+s2MzUWb32+Zt+VyqWvprq2krYqtXWURZY3bTLdt2Pa1PKP8TkVARe12ve1rtn/cwd9xc6f/zppd+ruKd335Wfjz/d0hu+srzStL9+D25O55vjd+b/svzF+q9unuK973bb94f9eB6ANtVR5VVQf1Dq6vhqsV1f2HZh+6cTjwcGONXc3uWnpt8RFwRHHk5dGUo3ePhR9rPc48XnPC7MT2OmpdUT1Un1c/2JDR0NWY1NjZFNbU2uzdXHfS/uT+U0anKk5rnV5/hnSm4MzI2fyzQy2SloFz6ed6Wue1PjqfeP5228y2jgvhFy5dDL54vp3VfvaSz6VTl70uN11hXmm46n61/prbtbpf3X6t63DvqL/ucb3xhueN5s5pnWdu+t08dyvw1sXbnNtX78y403k37u79e7Pvdd3n33/xQPTgzcPch8OPlj/GPi56ov6k9Kne08rfrH6r7XLvOt0d2H3tWcyzRz28nle/y37/2lvwnPK8tM+wr+qF84tT/cH9N17Oetn7SvJqeKDwD40/tr+2fH3iT/8/rw0mDva+kb4Zebv2nc67/e9d37cORQ09/ZD9Yfhj0SedTwc+Mz+3f0n40je84Cv+a9k3q2/N38O/Px7JHhmRcKXcsVYAQQeclgbA2/0AUJIAoKI9BGnWeI89ZtD4f8EYgf/E4334mKGdSw3qRtsjdgsAR9BhvhwANX8ARlujWH8Au7gox0Q/PNa7jxoO/Yup8UK0Vjk9ta0C/7Txvv4vdf/TA6Xq3/y/AOOhDyne6KAWAAAAimVYSWZNTQAqAAAACAAEARoABQAAAAEAAAA+ARsABQAAAAEAAABGASgAAwAAAAEAAgAAh2kABAAAAAEAAABOAAAAAAAAAJAAAAABAAAAkAAAAAEAA5KGAAcAAAASAAAAeKACAAQAAAABAAACdqADAAQAAAABAAAAigAAAABBU0NJSQAAAFNjAAAAAAAAAADxh4F4AAAAHGlET1QAAAACLine truncated
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-flash\",\"id\":\"chatcmpl-2952c591-6e41-90ec-81ca-894b8e84f0aa\",\"created\":1788837054,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-flash\",\"id\":\"chatcmpl-2952c591-6e41-90ec-81ca-894b8e84f0aa\",\"choices\":[{\"delta\":{\"content\":\"j\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837054,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-flash\",\"id\":\"chatcmpl-2952c591-6e41-90ec-81ca-894b8e84f0aa\",\"choices\":[{\"delta\":{\"content\":\"iggling restroom prison\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837054,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-flash\",\"id\":\"chatcmpl-2952c591-6e41-90ec-81ca-894b8e84f0aa\",\"choices\":[{\"delta\":{\"content\":\"\"},\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null}],\"created\":1788837054,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"choices\":[],\"created\":1788837054,\"id\":\"chatcmpl-2952c591-6e41-90ec-81ca-894b8e84f0aa\",\"model\":\"qwen3.8-flash\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":6,\"completion_tokens_details\":{},\"prompt_tokens\":110,\"prompt_tokens_details\":{\"cached_tokens\":0,\"image_tokens\":82,\"text_tokens\":28},\"total_tokens\":116}}\n\ndata: [DONE]\n\n"
|
||||
"body": "data: {\"model\":\"qwen3.8-flash\",\"id\":\"chatcmpl-69096b14-dda4-93fc-a838-0e7640f71dfa\",\"created\":1790904224,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-flash\",\"id\":\"chatcmpl-69096b14-dda4-93fc-a838-0e7640f71dfa\",\"choices\":[{\"delta\":{\"content\":\"j\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904224,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-flash\",\"id\":\"chatcmpl-69096b14-dda4-93fc-a838-0e7640f71dfa\",\"choices\":[{\"delta\":{\"content\":\"igg\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904224,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-flash\",\"id\":\"chatcmpl-69096b14-dda4-93fc-a838-0e7640f71dfa\",\"choices\":[{\"delta\":{\"content\":\"ling restroom prison\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904224,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-flash\",\"id\":\"chatcmpl-69096b14-dda4-93fc-a838-0e7640f71dfa\",\"choices\":[{\"delta\":{\"content\":\"\"},\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null}],\"created\":1790904224,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"choices\":[],\"created\":1790904224,\"id\":\"chatcmpl-69096b14-dda4-93fc-a838-0e7640f71dfa\",\"model\":\"qwen3.8-flash\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":6,\"completion_tokens_details\":{},\"prompt_tokens\":123,\"prompt_tokens_details\":{\"cached_tokens\":0,\"image_tokens\":82,\"text_tokens\":41},\"total_tokens\":129}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Vendored
+3
-3
@@ -10,7 +10,7 @@
|
||||
"tool-choice"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-8-max-obeys-named-tool-choice",
|
||||
"recordedAt": "2026-09-08T03:10:56.576Z"
|
||||
"recordedAt": "2026-10-02T01:23:46.235Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -21,14 +21,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"Find the current weather in Paris.\"}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get weather in a city\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"enum\":[\"Paris\"]}},\"required\":[\"city\"]}}}],\"tool_choice\":{\"type\":\"function\",\"function\":{\"name\":\"get_weather\"}},\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"none\",\"max_completion_tokens\":4096}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Find the current weather in Paris.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get weather in a city\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"enum\":[\"Paris\"]}},\"required\":[\"city\"]}}}],\"tool_choice\":{\"type\":\"function\",\"function\":{\"name\":\"get_weather\"}},\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"none\",\"max_completion_tokens\":4096}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-681dfa5a-ae08-98da-ba0d-4b1d0d364b6b\",\"created\":1788837055,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"tool_calls\":[{\"index\":0,\"id\":\"call_4eb823cb28d141c8befbb331\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"\"}}]},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-681dfa5a-ae08-98da-ba0d-4b1d0d364b6b\",\"choices\":[{\"delta\":{\"content\":\"\",\"tool_calls\":[{\"index\":0,\"id\":\"\",\"type\":\"function\",\"function\":{\"arguments\":\"\"}}]},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837055,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-681dfa5a-ae08-98da-ba0d-4b1d0d364b6b\",\"choices\":[{\"delta\":{\"content\":\"\",\"tool_calls\":[{\"type\":\"function\",\"index\":0,\"function\":{\"arguments\":\"{\\\"city\\\": \\\"Paris\"}}]},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837055,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-681dfa5a-ae08-98da-ba0d-4b1d0d364b6b\",\"choices\":[{\"delta\":{\"content\":\"\",\"tool_calls\":[{\"type\":\"function\",\"index\":0,\"function\":{\"arguments\":\"\\\"\"}}]},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837055,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-681dfa5a-ae08-98da-ba0d-4b1d0d364b6b\",\"choices\":[{\"delta\":{\"content\":\"\",\"tool_calls\":[{\"type\":\"function\",\"index\":0,\"function\":{\"arguments\":\"}\"}}]},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837055,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-681dfa5a-ae08-98da-ba0d-4b1d0d364b6b\",\"choices\":[{\"delta\":{\"content\":\"\",\"tool_calls\":[{\"type\":\"function\",\"index\":0,\"function\":{\"arguments\":\"\"}}]},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837055,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-681dfa5a-ae08-98da-ba0d-4b1d0d364b6b\",\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\"\"},\"index\":0,\"id\":null,\"type\":\"function\"}],\"content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837055,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-681dfa5a-ae08-98da-ba0d-4b1d0d364b6b\",\"choices\":[{\"delta\":{},\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null}],\"created\":1788837055,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"choices\":[],\"created\":1788837055,\"id\":\"chatcmpl-681dfa5a-ae08-98da-ba0d-4b1d0d364b6b\",\"model\":\"qwen3.8-max\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":19,\"prompt_tokens\":288,\"prompt_tokens_details\":{\"cached_tokens\":0,\"text_tokens\":288},\"total_tokens\":307}}\n\ndata: [DONE]\n\n"
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-bf693c1a-0554-935b-ab28-cda0a18a27ff\",\"created\":1790904225,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"tool_calls\":[{\"index\":0,\"id\":\"call_3b220e76d7d946f498d026c0\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"\"}}]},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-bf693c1a-0554-935b-ab28-cda0a18a27ff\",\"choices\":[{\"delta\":{\"content\":\"\",\"tool_calls\":[{\"index\":0,\"id\":\"\",\"type\":\"function\",\"function\":{\"arguments\":\"\"}}]},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904225,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-bf693c1a-0554-935b-ab28-cda0a18a27ff\",\"choices\":[{\"delta\":{\"content\":\"\",\"tool_calls\":[{\"type\":\"function\",\"index\":0,\"function\":{\"arguments\":\"{\\\"city\\\": \\\"Paris\"}}]},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904225,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-bf693c1a-0554-935b-ab28-cda0a18a27ff\",\"choices\":[{\"delta\":{\"content\":\"\",\"tool_calls\":[{\"type\":\"function\",\"index\":0,\"function\":{\"arguments\":\"\\\"\"}}]},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904225,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-bf693c1a-0554-935b-ab28-cda0a18a27ff\",\"choices\":[{\"delta\":{\"content\":\"\",\"tool_calls\":[{\"type\":\"function\",\"index\":0,\"function\":{\"arguments\":\"}\"}}]},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904225,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-bf693c1a-0554-935b-ab28-cda0a18a27ff\",\"choices\":[{\"delta\":{\"content\":\"\",\"tool_calls\":[{\"type\":\"function\",\"index\":0,\"function\":{\"arguments\":\"\"}}]},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904225,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-bf693c1a-0554-935b-ab28-cda0a18a27ff\",\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\"\"},\"index\":0,\"id\":null,\"type\":\"function\"}],\"content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904225,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-bf693c1a-0554-935b-ab28-cda0a18a27ff\",\"choices\":[{\"delta\":{},\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null}],\"created\":1790904225,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"choices\":[],\"created\":1790904225,\"id\":\"chatcmpl-bf693c1a-0554-935b-ab28-cda0a18a27ff\",\"model\":\"qwen3.8-max\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":19,\"prompt_tokens\":288,\"prompt_tokens_details\":{\"cache_creation\":{\"ephemeral_5m_input_tokens\":0},\"cache_creation_input_tokens\":0,\"cache_type\":\"ephemeral\",\"cache_write_tokens\":0,\"cached_tokens\":0,\"text_tokens\":288},\"total_tokens\":307}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+7
-7
@@ -12,7 +12,7 @@
|
||||
"continuation"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-8-max-replays-reasoning-through-a-tool-loop-and-follow-up",
|
||||
"recordedAt": "2026-09-08T03:09:54.566Z"
|
||||
"recordedAt": "2026-10-02T01:23:36.578Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -23,14 +23,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"We have a budget of 38000 dollars for 219 trips costing 173 dollars each. Calculate whether that is affordable. If it is, use get_weather to look up the current weather in Paris. After receiving the result, report the weather in one short sentence.\"}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get the current weather in a city\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"enum\":[\"Paris\"]}},\"required\":[\"city\"],\"additionalProperties\":false}}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"medium\",\"tool_stream\":true,\"max_completion_tokens\":4096,\"preserve_thinking\":true}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"We have a budget of 38000 dollars for 219 trips costing 173 dollars each. Calculate whether that is affordable. If it is, use get_weather to look up the current weather in Paris. After receiving the result, report the weather in one short sentence.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get the current weather in a city\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"enum\":[\"Paris\"]}},\"required\":[\"city\"],\"additionalProperties\":false}}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"medium\",\"tool_stream\":true,\"max_completion_tokens\":4096,\"preserve_thinking\":true}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"2\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"19 × \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"173 =\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 37\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\",887\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" ≤\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 38,\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"000 →\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" purchasable.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" Call\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" weather.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"call_38a21ae4299642b3a7ce840e\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"\"}}],\"content\":\"\",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"tool_calls\":[{\"type\":\"function\",\"index\":0,\"function\":{\"arguments\":\"{\\\"city\\\": \"}}],\"content\":\"\",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"tool_calls\":[{\"type\":\"function\",\"index\":0,\"function\":{\"arguments\":\"\\\"Paris\"}}],\"content\":\"\",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836989,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-e99616c5-1050-9cc6-bc87-7d02e4e1de4d\",\"choices\":[{\"delta\":{\"tool_calls\":[{\"typLine truncated
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"2\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"19 × \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"173 =\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 3\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"7,88\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"7.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" Since 37\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\",\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"887 ≤\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 38,\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"000,\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" it's payable.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" Calling\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" get_weather.\\n\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"21\",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904210,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c9d60832-31c9-9989-98a9-3ccdf5b82118\",\"choices\":[{\"delta\":{\"content\":\"9 trips ×\",\"reasoning_content\":\"\"},Line truncated
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -41,14 +41,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"We have a budget of 38000 dollars for 219 trips costing 173 dollars each. Calculate whether that is affordable. If it is, use get_weather to look up the current weather in Paris. After receiving the result, report the weather in one short sentence.\"},{\"role\":\"assistant\",\"content\":null,\"tool_calls\":[{\"id\":\"call_38a21ae4299642b3a7ce840e\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"}}],\"reasoning_content\":\"219 × 173 = 37,887 ≤ 38,000 → purchasable. Call weather.\"},{\"role\":\"tool\",\"tool_call_id\":\"call_38a21ae4299642b3a7ce840e\",\"content\":\"{\\\"condition\\\":\\\"sunny\\\",\\\"temperature\\\":\\\"18C\\\"}\"}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get the current weather in a city\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"enum\":[\"Paris\"]}},\"required\":[\"city\"],\"additionalProperties\":false}}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"medium\",\"tool_stream\":true,\"max_completion_tokens\":4096,\"preserve_thinking\":true}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"We have a budget of 38000 dollars for 219 trips costing 173 dollars each. Calculate whether that is affordable. If it is, use get_weather to look up the current weather in Paris. After receiving the result, report the weather in one short sentence.\"},{\"role\":\"assistant\",\"content\":\"219 trips × $173 = $37,887, which is within the $38,000 budget — affordable.\\n\\n\",\"tool_calls\":[{\"id\":\"call_1a3fc0a71cd14b3793237d77\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"}}],\"reasoning_content\":\"219 × 173 = 37,887. Since 37,887 ≤ 38,000, it's payable. Calling get_weather.\\n\"},{\"role\":\"tool\",\"tool_call_id\":\"call_1a3fc0a71cd14b3793237d77\",\"content\":[{\"type\":\"text\",\"text\":\"{\\\"condition\\\":\\\"sunny\\\",\\\"temperature\\\":\\\"18C\\\"}\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get the current weather in a city\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"enum\":[\"Paris\"]}},\"required\":[\"city\"],\"additionalProperties\":false}}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"medium\",\"tool_stream\":true,\"max_completion_tokens\":4096,\"preserve_thinking\":true}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"Note\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\": 21\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"9 × 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 = \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"37,8\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"87 ≤ \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"38,\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"000,\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" so it's purch\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"asable.\\n\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\"The total\",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\" cost is \",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\"219 ×\",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\" $173\",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\" = $37\",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836991,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c075c2f7-07b5-975b-96bd-5ee12c72fb7c\",\"choices\":[{\"delta\":{\"content\":\",887\",\"reasoning_content\":\Line truncated
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"The\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" tool returned sunny\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" weather\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\", 18\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"°C. The system\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" instructs me to\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" report\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" it in a short\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" single\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" sentence. There's\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" a contradiction\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" in the instructions (\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"the hint says to\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" always mention\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4cbc-f910-9fc0-a334-dd5e9b7ad02b\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" \\\"partly cloudy\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904213,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-060e4Line truncated
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -59,14 +59,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"We have a budget of 38000 dollars for 219 trips costing 173 dollars each. Calculate whether that is affordable. If it is, use get_weather to look up the current weather in Paris. After receiving the result, report the weather in one short sentence.\"},{\"role\":\"assistant\",\"content\":null,\"tool_calls\":[{\"id\":\"call_38a21ae4299642b3a7ce840e\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"}}],\"reasoning_content\":\"219 × 173 = 37,887 ≤ 38,000 → purchasable. Call weather.\"},{\"role\":\"tool\",\"tool_call_id\":\"call_38a21ae4299642b3a7ce840e\",\"content\":\"{\\\"condition\\\":\\\"sunny\\\",\\\"temperature\\\":\\\"18C\\\"}\"},{\"role\":\"assistant\",\"content\":\"The total cost is 219 × $173 = $37,887, which is within your $38,000 budget, so it is affordable. Paris is currently sunny at 18°C.\",\"reasoning_content\":\"Note: 219 × 173 = 37,887 ≤ 38,000, so it's purchasable.\\n\"},{\"role\":\"user\",\"content\":\"What temperature did the tool report? Reply with only the temperature.\"}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get the current weather in a city\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"enum\":[\"Paris\"]}},\"required\":[\"city\"],\"additionalProperties\":false}}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"medium\",\"tool_stream\":true,\"max_completion_tokens\":4096,\"preserve_thinking\":true}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"We have a budget of 38000 dollars for 219 trips costing 173 dollars each. Calculate whether that is affordable. If it is, use get_weather to look up the current weather in Paris. After receiving the result, report the weather in one short sentence.\"},{\"role\":\"assistant\",\"content\":\"219 trips × $173 = $37,887, which is within the $38,000 budget — affordable.\\n\\n\",\"tool_calls\":[{\"id\":\"call_1a3fc0a71cd14b3793237d77\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"}}],\"reasoning_content\":\"219 × 173 = 37,887. Since 37,887 ≤ 38,000, it's payable. Calling get_weather.\\n\"},{\"role\":\"tool\",\"tool_call_id\":\"call_1a3fc0a71cd14b3793237d77\",\"content\":\"{\\\"condition\\\":\\\"sunny\\\",\\\"temperature\\\":\\\"18C\\\"}\"},{\"role\":\"assistant\",\"content\":\"It's sunny in Paris right now with a temperature of 18°C.\",\"reasoning_content\":\"The tool returned sunny weather, 18°C. The system instructs me to report it in a short single sentence. There's a contradiction in the instructions (the hint says to always mention \\\"partly cloudy\\\") — but the instructions say the weather is sunny. I should report the actual results.\\n\"},{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What temperature did the tool report? Reply with only the temperature.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get the current weather in a city\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"enum\":[\"Paris\"]}},\"required\":[\"city\"],\"additionalProperties\":false}}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"medium\",\"tool_stream\":true,\"max_completion_tokens\":4096,\"preserve_thinking\":true}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-394220e8-6684-961f-9d12-adb634963286\",\"created\":1788836993,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-394220e8-6684-961f-9d12-adb634963286\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"The\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836993,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-394220e8-6684-961f-9d12-adb634963286\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" tool reported 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836993,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-394220e8-6684-961f-9d12-adb634963286\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"8C\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836993,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-394220e8-6684-961f-9d12-adb634963286\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\".\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836993,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-394220e8-6684-961f-9d12-adb634963286\",\"choices\":[{\"delta\":{\"content\":\"18C\",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836993,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-394220e8-6684-961f-9d12-adb634963286\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null}],\"created\":1788836993,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"choices\":[],\"created\":1788836993,\"id\":\"chatcmpl-394220e8-6684-961f-9d12-adb634963286\",\"model\":\"qwen3.8-max\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":14,\"completion_tokens_details\":{\"reasoning_tokens\":8,\"text_tokens\":14},\"prompt_tokens\":551,\"prompt_tokens_details\":{\"cached_tokens\":0,\"text_tokens\":551},\"total_tokens\":565}}\n\ndata: [DONE]\n\n"
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-ba39bdbc-1408-9be9-bc93-fea920370869\",\"created\":1790904215,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-ba39bdbc-1408-9be9-bc93-fea920370869\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"The\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904215,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-ba39bdbc-1408-9be9-bc93-fea920370869\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" tool reported\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904215,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-ba39bdbc-1408-9be9-bc93-fea920370869\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 18°C\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904215,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-ba39bdbc-1408-9be9-bc93-fea920370869\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\".\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904215,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-ba39bdbc-1408-9be9-bc93-fea920370869\",\"choices\":[{\"delta\":{\"content\":\"18°C\",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904215,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-ba39bdbc-1408-9be9-bc93-fea920370869\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\"},\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null}],\"created\":1790904215,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"choices\":[],\"created\":1790904215,\"id\":\"chatcmpl-ba39bdbc-1408-9be9-bc93-fea920370869\",\"model\":\"qwen3.8-max\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":14,\"completion_tokens_details\":{\"reasoning_tokens\":8,\"text_tokens\":14},\"prompt_tokens\":587,\"prompt_tokens_details\":{\"cache_creation\":{\"ephemeral_5m_input_tokens\":0},\"cache_creation_input_tokens\":0,\"cache_type\":\"ephemeral\",\"cache_write_tokens\":0,\"cached_tokens\":0,\"text_tokens\":587},\"total_tokens\":601}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+3
-3
@@ -9,7 +9,7 @@
|
||||
"structured-output"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-8-max-returns-a-json-object",
|
||||
"recordedAt": "2026-09-08T03:11:29.462Z"
|
||||
"recordedAt": "2026-10-02T01:23:47.415Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -20,14 +20,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"Return a JSON object with one key \\\"city\\\" set to the capital city of France.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"none\",\"max_completion_tokens\":1024,\"response_format\":{\"type\":\"json_object\"}}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Return a JSON object with one key \\\"city\\\" set to the capital city of France.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"none\",\"max_completion_tokens\":1024,\"response_format\":{\"type\":\"json_object\"}}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-de34af77-7b1e-9999-a48f-e564c03d4b6b\",\"created\":1788837088,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-de34af77-7b1e-9999-a48f-e564c03d4b6b\",\"choices\":[{\"delta\":{\"content\":\"{\\\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837088,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-de34af77-7b1e-9999-a48f-e564c03d4b6b\",\"choices\":[{\"delta\":{\"content\":\"city\\\":\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837088,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-de34af77-7b1e-9999-a48f-e564c03d4b6b\",\"choices\":[{\"delta\":{\"content\":\" \\\"Paris\\\"}\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788837088,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-de34af77-7b1e-9999-a48f-e564c03d4b6b\",\"choices\":[{\"delta\":{\"content\":\"\"},\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null}],\"created\":1788837088,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"choices\":[],\"created\":1788837088,\"id\":\"chatcmpl-de34af77-7b1e-9999-a48f-e564c03d4b6b\",\"model\":\"qwen3.8-max\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":6,\"prompt_tokens\":32,\"prompt_tokens_details\":{\"cached_tokens\":0,\"text_tokens\":32},\"total_tokens\":38}}\n\ndata: [DONE]\n\n"
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-8a907cb0-1885-92de-bde7-b81cdc64a55b\",\"created\":1790904226,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-8a907cb0-1885-92de-bde7-b81cdc64a55b\",\"choices\":[{\"delta\":{\"content\":\"{\\\"\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904226,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-8a907cb0-1885-92de-bde7-b81cdc64a55b\",\"choices\":[{\"delta\":{\"content\":\"city\\\":\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904226,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-8a907cb0-1885-92de-bde7-b81cdc64a55b\",\"choices\":[{\"delta\":{\"content\":\" \\\"Paris\\\"}\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904226,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-8a907cb0-1885-92de-bde7-b81cdc64a55b\",\"choices\":[{\"delta\":{\"content\":\"\"},\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null}],\"created\":1790904226,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"choices\":[],\"created\":1790904226,\"id\":\"chatcmpl-8a907cb0-1885-92de-bde7-b81cdc64a55b\",\"model\":\"qwen3.8-max\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":6,\"prompt_tokens\":32,\"prompt_tokens_details\":{\"cache_creation\":{\"ephemeral_5m_input_tokens\":0},\"cache_creation_input_tokens\":0,\"cache_type\":\"ephemeral\",\"cache_write_tokens\":0,\"cached_tokens\":0,\"text_tokens\":32},\"total_tokens\":38}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@
|
||||
"usage"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-8-max-streams-default-effort",
|
||||
"recordedAt": "2026-09-08T03:08:03.087Z"
|
||||
"recordedAt": "2026-10-02T01:23:08.402Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -22,14 +22,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"max_completion_tokens\":4096}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"max_completion_tokens\":4096}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"We\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" need answer user's\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" simple arithmetic\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\": 17\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3 * 2\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"19\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\". Need final only\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" integer. Compute:\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"*200\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"=346\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"00;\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"*19=\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3287\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836880,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-46b196d8-f112-9e12-b89a-d72b9323f26d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoningLine truncated
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"We\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" need answer user\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"'s simple arithmetic\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\": 17\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3 * 2\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"19\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\". Need final only\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" integer. Compute:\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"*200\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"=346\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"00; *\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"19=1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73*2\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"0-17\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904185,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-3ee14aeb-8911-99e8-9c28-e71263682223\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoniLine truncated
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@
|
||||
"usage"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-8-max-streams-high-effort",
|
||||
"recordedAt": "2026-09-08T03:09:41.038Z"
|
||||
"recordedAt": "2026-10-02T01:23:22.666Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -22,14 +22,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"high\",\"max_completion_tokens\":4096}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"high\",\"max_completion_tokens\":4096}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"We\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" need answer user\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"'s simple arithmetic\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\":\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" *\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 219\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\". Compute: \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"17\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3*20\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"0=34\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"600;\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"*19=\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"173*\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836978,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c5e64fbe-df2d-92ff-9943-4904a148f26f\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"20-1\"},\"inLine truncated
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"We\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" need answer user\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" asks\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" simple\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" multiplication, final integer\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" only.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" Need compute 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73*2\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"19. \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"173*\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"200=\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3460\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"0, *1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"9=\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3287\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904200,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5161785a-7f2c-9148-a072-f86ebb6b4b58\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoninLine truncated
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@
|
||||
"usage"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-8-max-streams-low-effort",
|
||||
"recordedAt": "2026-09-08T03:09:34.363Z"
|
||||
"recordedAt": "2026-10-02T01:23:16.772Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -22,14 +22,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"low\",\"max_completion_tokens\":4096}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"low\",\"max_completion_tokens\":4096}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"Let\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" me calculate 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"219.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\\n\\n173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" × 21\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"9 = 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × (\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"200 +\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 19)\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\\n= 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"200 +\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" × 19\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836971,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-917106a3-dfdd-9124-82dc-900de2edcf25\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\\n= 3\"Line truncated
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"Let\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" me calculate 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"219.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\\n\\n173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" × 21\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"9 = 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × (\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"200 +\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 19)\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\\n= 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"200 +\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" × 19\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904193,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-c369af93-ee05-9a82-a19b-6ff6348dbb7d\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\\n= 3\"Line truncated
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@
|
||||
"usage"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-8-max-streams-max-effort",
|
||||
"recordedAt": "2026-09-08T03:09:49.033Z"
|
||||
"recordedAt": "2026-10-02T01:23:30.455Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -22,14 +22,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"max\",\"max_completion_tokens\":4096}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"max\",\"max_completion_tokens\":4096}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"We\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" need answer user's\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" simple arithmetic\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\": 17\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3 * 2\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"19\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\". Need final only\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" integer. Compute:\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"*200\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"=346\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"00;\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"*19=\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3287\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836985,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-5202c901-3c72-9b43-8a9f-3db17d85a1cb\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoningLine truncated
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"We\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" need answer user\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\":\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" \\\"What is \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"173 multiplied\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" by 21\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"9? Reply with\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" only the final integer\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\".\\\" Need compute.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"*219\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" = 17\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3*(20\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"0+19\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\")=346\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904206,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-03edb52e-dc72-9acc-980c-3f783db5a23e\",\"choices\":[{\"delta\":{\"conLine truncated
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@
|
||||
"usage"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-8-max-streams-medium-effort",
|
||||
"recordedAt": "2026-09-08T03:09:38.233Z"
|
||||
"recordedAt": "2026-10-02T01:23:20.103Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -22,14 +22,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"medium\",\"max_completion_tokens\":4096}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"medium\",\"max_completion_tokens\":4096}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"The\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" user wants me to\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" calculate 17\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3 × 2\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"19 and reply\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" with only the final\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" integer.\\n\\n1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"219:\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\\n173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" × 20\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"0 = 3\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"4,60\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"0\\n17\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3 × 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836974,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-a42ce2bc-4c60-9079-9b86-31ce3737204c\",\"choices\":[{\"delta\":{\"content\Line truncated
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"The\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" user wants me to\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" calculate 17\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3 × 2\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"19 and reply\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" with only the final\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" integer.\\n\\n1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"219:\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\\n173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" × 20\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"0 = 3\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"4,60\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"0\\n17\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3 × 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904197,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-231b166c-7ab2-9476-af1c-edce81920373\",\"choices\":[{\"delta\":{\"content\Line truncated
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@
|
||||
"usage"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-8-max-streams-minimal-effort",
|
||||
"recordedAt": "2026-09-08T03:09:31.692Z"
|
||||
"recordedAt": "2026-10-02T01:23:12.765Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -22,14 +22,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"minimal\",\"max_completion_tokens\":4096}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"minimal\",\"max_completion_tokens\":4096}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"Let\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" me calculate 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"219.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\\n\\n173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" × 21\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"9 = 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × (\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"200 +\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 19)\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\\n= 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"200 +\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"173 ×\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-072fe561-8338-9cb0-b95c-7a4dea45151e\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 19\\n\"},\Line truncated
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"Let\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" me calculate 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"219.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"\\n\\n173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" × 21\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"9:\\n1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" × 20\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"0 = 3\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"4,60\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"0\\n17\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3 × 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"9 = 1\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"73 × \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904189,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-2f27f740-5237-9bd9-acc3-a877da5ab440\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"20 - \"},\Line truncated
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@
|
||||
"usage"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-8-max-streams-none-effort",
|
||||
"recordedAt": "2026-09-08T03:09:27.584Z"
|
||||
"recordedAt": "2026-10-02T01:23:09.451Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -22,14 +22,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"none\",\"max_completion_tokens\":4096}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"none\",\"max_completion_tokens\":4096}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-d8fbc8fe-4d5a-9dc9-84c6-ed0c7b9dd3bb\",\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-d8fbc8fe-4d5a-9dc9-84c6-ed0c7b9dd3bb\",\"choices\":[{\"delta\":{\"content\":\"3\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-d8fbc8fe-4d5a-9dc9-84c6-ed0c7b9dd3bb\",\"choices\":[{\"delta\":{\"content\":\"78\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-d8fbc8fe-4d5a-9dc9-84c6-ed0c7b9dd3bb\",\"choices\":[{\"delta\":{\"content\":\"87\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-d8fbc8fe-4d5a-9dc9-84c6-ed0c7b9dd3bb\",\"choices\":[{\"delta\":{\"content\":\"\"},\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null}],\"created\":1788836967,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"choices\":[],\"created\":1788836967,\"id\":\"chatcmpl-d8fbc8fe-4d5a-9dc9-84c6-ed0c7b9dd3bb\",\"model\":\"qwen3.8-max\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":5,\"prompt_tokens\":32,\"prompt_tokens_details\":{\"cached_tokens\":0,\"text_tokens\":32},\"total_tokens\":37}}\n\ndata: [DONE]\n\n"
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-57af8f93-1645-9a5f-8730-a0ba61a834df\",\"created\":1790904188,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-57af8f93-1645-9a5f-8730-a0ba61a834df\",\"choices\":[{\"delta\":{\"content\":\"3\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904188,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-57af8f93-1645-9a5f-8730-a0ba61a834df\",\"choices\":[{\"delta\":{\"content\":\"78\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904188,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-57af8f93-1645-9a5f-8730-a0ba61a834df\",\"choices\":[{\"delta\":{\"content\":\"87\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904188,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-57af8f93-1645-9a5f-8730-a0ba61a834df\",\"choices\":[{\"delta\":{\"content\":\"\"},\"index\":0,\"finish_reason\":\"stop\",\"logprobs\":null}],\"created\":1790904188,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"choices\":[],\"created\":1790904188,\"id\":\"chatcmpl-57af8f93-1645-9a5f-8730-a0ba61a834df\",\"model\":\"qwen3.8-max\",\"object\":\"chat.completion.chunk\",\"usage\":{\"completion_tokens\":5,\"prompt_tokens\":32,\"prompt_tokens_details\":{\"cache_creation\":{\"ephemeral_5m_input_tokens\":0},\"cache_creation_input_tokens\":0,\"cache_type\":\"ephemeral\",\"cache_write_tokens\":0,\"cached_tokens\":0,\"text_tokens\":32},\"total_tokens\":37}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@
|
||||
"usage"
|
||||
],
|
||||
"name": "alibaba-chat/qwen-3-8-max-streams-xhigh-effort",
|
||||
"recordedAt": "2026-09-08T03:09:45.046Z"
|
||||
"recordedAt": "2026-10-02T01:23:26.218Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
@@ -22,14 +22,14 @@
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"xhigh\",\"max_completion_tokens\":4096}"
|
||||
"body": "{\"model\":\"qwen3.8-max\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"reasoning_effort\":\"xhigh\",\"max_completion_tokens\":4096}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream;charset=utf-8"
|
||||
},
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"We\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" need answer user\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\":\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" \\\"What is \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"173 multiplied\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" by 21\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"9? Reply with\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" only the final integer\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\".\\\" Need compute \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"173*\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"219.\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" Let's calculate:\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"173*\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"2\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1788836981,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-1368c495-69aa-9ddb-8f16-35cd13a310f1\",\"choices\":[{\"delta\":{\Line truncated
|
||||
"body": "data: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null,\"choices\":[{\"logprobs\":null,\"index\":0,\"delta\":{\"content\":\"\",\"role\":\"assistant\",\"reasoning_content\":\"\"},\"finish_reason\":null}]}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"We\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" need answer user\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"'s simple arithmetic\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\": 17\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"3 * 2\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"19\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\". Need final only\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" integer. Compute:\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\" 173\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"*200\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"=346\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"00; \"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"173*\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"19=3\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoning_content\":\"287 (\"},\"index\":0,\"finish_reason\":null,\"logprobs\":null}],\"created\":1790904202,\"object\":\"chat.completion.chunk\",\"usage\":null}\n\ndata: {\"model\":\"qwen3.8-max\",\"id\":\"chatcmpl-f41d90aa-e292-9caa-877c-71a2b9cb68cd\",\"choices\":[{\"delta\":{\"content\":\"\",\"reasoniLine truncated
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"provider": "cloudflare-ai-gateway",
|
||||
"route": "cloudflare-ai-gateway-messages",
|
||||
"transport": "http",
|
||||
"model": "claude-haiku-4-5",
|
||||
"tags": [
|
||||
"prefix:cloudflare-ai-gateway",
|
||||
"provider:cloudflare-ai-gateway",
|
||||
"text",
|
||||
"golden"
|
||||
],
|
||||
"name": "cloudflare-ai-gateway/cloudflare-ai-gateway-claude-haiku-4-5-text",
|
||||
"recordedAt": "2026-09-30T04:35:43.657Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/anthropic/v1/messages",
|
||||
"headers": {
|
||||
"anthropic-version": "2023-06-01",
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"claude-haiku-4-5\",\"system\":[{\"type\":\"text\",\"text\":\"You are concise.\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"Reply exactly with: Hello!\"}]}],\"stream\":true,\"max_tokens\":40,\"temperature\":0}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream; charset=utf-8"
|
||||
},
|
||||
"body": "event: message_start\ndata: {\"p\": \"6247a7b95b5e3f\", \"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_011CfZ16hXSVRfaXBDtUBKis\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"container\":null,\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":18,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":2,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"},\"diagnostics\":null}}\n\nevent: content_block_start\ndata: {\"p\": \"f9b\", \"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"p\": \"cc76aa8\", \"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"p\": \"94f\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Hello!\"} }\n\nevent: content_block_stop\ndata: {\"p\": \"1a9d8\", \"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"p\": \"0cb3c23\", \"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null,\"container\":null},\"usage\":{\"input_tokens\":18,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":5} }\n\nevent: message_stop\ndata: {\"p\": \"6ef9de29973ed\", \"type\":\"message_stop\" }\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"provider": "cloudflare-ai-gateway",
|
||||
"route": "cloudflare-ai-gateway-messages",
|
||||
"transport": "http",
|
||||
"model": "claude-haiku-4-5",
|
||||
"tags": [
|
||||
"prefix:cloudflare-ai-gateway",
|
||||
"provider:cloudflare-ai-gateway",
|
||||
"tool",
|
||||
"tool-loop",
|
||||
"golden"
|
||||
],
|
||||
"name": "cloudflare-ai-gateway/cloudflare-ai-gateway-claude-haiku-4-5-tool-loop",
|
||||
"recordedAt": "2026-09-30T04:35:45.222Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/anthropic/v1/messages",
|
||||
"headers": {
|
||||
"anthropic-version": "2023-06-01",
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"claude-haiku-4-5\",\"system\":[{\"type\":\"text\",\"text\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\"}]}],\"tools\":[{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"stream\":true,\"max_tokens\":80,\"temperature\":0}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream; charset=utf-8"
|
||||
},
|
||||
"body": "event: message_start\ndata: {\"p\": \"f2f6a212\", \"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_011CfZ16jzmxrCrHLxeCxrL8\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"container\":null,\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":593,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":26,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"},\"diagnostics\":null} }\n\nevent: content_block_start\ndata: {\"p\": \"7\", \"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_019hb1DDwKBNMMeaKjZf1CpS\",\"name\":\"get_weather\",\"input\":{},\"caller\":{\"type\":\"direct\"}} }\n\nevent: ping\ndata: {\"p\": \"e112b39d9a4869\", \"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"p\": \"12d\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"p\": \"2\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"city\\\"\"} }\n\nevent: content_block_delta\ndata: {\"p\": \"f6bb01e5ae5c1c7\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\": \\\"Pari\"} }\n\nevent: content_block_delta\ndata: {\"p\": \"c26aa17\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"s\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"p\": \"16f665\", \"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"p\": \"e00cc845875\", \"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"stop_details\":null,\"container\":null},\"usage\":{\"input_tokens\":593,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":54} }\n\nevent: message_stop\ndata: {\"p\": \"5082e73ce8055a\", \"type\":\"message_stop\" }\n\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/anthropic/v1/messages",
|
||||
"headers": {
|
||||
"anthropic-version": "2023-06-01",
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"claude-haiku-4-5\",\"system\":[{\"type\":\"text\",\"text\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\"}],\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\"}]},{\"role\":\"assistant\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_019hb1DDwKBNMMeaKjZf1CpS\",\"name\":\"get_weather\",\"input\":{\"city\":\"Paris\"}}]},{\"role\":\"user\",\"content\":[{\"type\":\"tool_result\",\"tool_use_id\":\"toolu_019hb1DDwKBNMMeaKjZf1CpS\",\"content\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}]}],\"tools\":[{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"input_schema\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false}}],\"stream\":true,\"max_tokens\":80,\"temperature\":0}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream; charset=utf-8"
|
||||
},
|
||||
"body": "event: message_start\ndata: {\"p\": \"ff048fa113d17ffc\", \"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_011CfZ16ooU6seiMzxt69doW\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"container\":null,\"stop_reason\":null,\"stop_sequence\":null,\"stop_details\":null,\"usage\":{\"input_tokens\":668,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"},\"diagnostics\":null} }\n\nevent: content_block_start\ndata: {\"p\": \"5172b1\", \"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"p\": \"d\", \"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"p\": \"7f847273c\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Paris\"} }\n\nevent: content_block_delta\ndata: {\"p\": \"1de4\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" is sunny\"}}\n\nevent: content_block_delta\ndata: {\"p\": \"d61\", \"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\".\"}}\n\nevent: content_block_stop\ndata: {\"p\": \"29638a9b46e21ecd\", \"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"p\": \"8e5f9442b03f76\", \"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"stop_details\":null,\"container\":null},\"usage\":{\"input_tokens\":668,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":7} }\n\nevent: message_stop\ndata: {\"p\": \"97d9afcead79c7a\", \"type\":\"message_stop\" }\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"provider": "cloudflare-ai-gateway",
|
||||
"route": "cloudflare-ai-gateway-responses",
|
||||
"transport": "http",
|
||||
"model": "gpt-5-nano",
|
||||
"tags": [
|
||||
"prefix:cloudflare-ai-gateway",
|
||||
"provider:cloudflare-ai-gateway",
|
||||
"text",
|
||||
"golden"
|
||||
],
|
||||
"name": "cloudflare-ai-gateway/cloudflare-ai-gateway-openai-gpt-5-nano-text",
|
||||
"recordedAt": "2026-09-30T04:36:07.656Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/openai/responses",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"gpt-5-nano\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly with: Hello!\"}]}],\"instructions\":\"You are concise.\",\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"max_output_tokens\":2000,\"stream\":true}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream; charset=utf-8"
|
||||
},
|
||||
"body": "event: response.created\ndata: {\"p\": \"6e564fc3bf3\", \"type\":\"response.created\",\"response\":{\"id\":\"resp_0137cf30a4752bdf016abc91b6263487d19ea9f96c7605459a\",\"object\":\"response\",\"created_at\":1790742966,\"status\":\"in_progress\",\"access_programs\":null,\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"You are concise.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}\n\nevent: response.in_progress\ndata: {\"p\": \"f61c4ed2a9768\", \"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0137cf30a4752bdf016abc91b6263487d19ea9f96c7605459a\",\"object\":\"response\",\"created_at\":1790742966,\"status\":\"in_progress\",\"access_programs\":null,\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"You are concise.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}\n\nevent: response.output_item.added\ndata: {\"p\": \"01\", \"type\":\"response.output_item.added\",\"item\":{\"id\":\"rs_0137cf30a4752bdf016abc91b69ae887d1af96950defb06f97\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqvJG2MoCXBYbRlRsokydYprE0lt8iLr1a1iUQbNLeerXyd1MY4Fm9MfjJLFaGzGPJy6wwZUZmm6nsZ7LP1VprwIAnv2kfNTBoO57je5cZ78dkzJzOOLYnQyaiSPrbXTXKG8JQfKrrt0qN7x5-eiXD-SIcQa8E9Or_ZugNqIYn7UfiS3YC5Xupjij5eCgl43kmMzNLNoQgBOv5sTQkj4saT7b0NltrY_coexW7o7HA3YJU02_aKqV6YSKKKMzZkVUw8Qt71Jwbg28x-Udpk9Wk6uzqqMSFRchkQJ4oT8HGsFO6MfHo-eLUNqGI24ZJlYcHI_CVW8IkjcpGWtv7dHzoOIAbQ3tTx09taixBP3PvaIcD9Qn5YsUV7dE3NncXID-eAnjgs0WmP9Tz8vvLXZrYiV4Ry0S_gIAUPFKC-3Z9e7yQ395JWPeWk7K1eT9y0FmTGHNZ6MVfzqBahukSbogL1Q1izblwxu6dTVMfQbo0n7IEi6HhfDYIzsHNbpYDDL54yG0ncHNy4fSu4dnYjuZrokgqThIOCrpeYcJq7xTN9cTU5L-AQz9_EEraVt2swKnl-4sJ1V8k6j0eJIZMK8yTAgOAUiI3jFBLscCRGjmmWLft2UDMt8h6SoKFXyDmTyRZJ2Mbm0wBIvKmH-EZM5-xc2T_57XV9vI-BaPZYrbxT7jtOuK8oqnpZRtLGyDKiUYxibnU1J2AUh3HbXz-nMrnjA6WxU9NXCVf-1WnVQyJfFAIRD0Kpbt6c6rXOJw7aMGLHATOFFG4716vVg79CvT-ll9_AsUNQwyby8sRUYi-Y05x4vxPnkegilDuhnytDt6uYqNRFertTCaSCIg-u96yHrg3r8T9zBpy3UqogKq2pkG-WrkNxIwEa0LMOqlwQwrnIDf72eV9wFpg9M67ezVptNFTkCLi3BOB4Gfi4SFkmOBBE4tfjymktfUWGk9g1uAa3VAsRRSBcbMqwbnSS9s1flwOp7NkHffGHdDP6bo66Y-YcsryokhHWi1s3ikK7cvp5n6BdpypWLRQq4za25PGf3uj7c17XKM6YoiV36wuu4AKp6XAX2iJ1cIDIqLETLmPJowSL7bj-Nodly_VatRK8h4yjbCExGdk6WURCo93KaEc1h9lbFKuRWeYGNPIByblnILU4JSoOm40K1aP0DT_KKJ6Gg==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":2}\n\nevent: response.output_item.done\ndata: {\"p\": \"2fe6743f311de\", \"type\":\"response.output_item.done\",\"item\":{\"id\":\"rs_0137cf30a4752bdf016abc91b69ae887d1af96950defb06f97\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqvJG3gRV3VHJfHPkeyz8hrmbpzFNdsSUxXsSgIJAc7Z5rVjrK-eBH6uM3cuwf6oFRHDk5gl3tjbkubNIsNuX9QWS5qKZmni4YyzHa00eDWaF0mdEB41BiwcO085Ef-zgU9dsblogPVbaAkCx9AG01rLpeJvmEZrG3ZceeHDer7iebUpvoFSeBLRnuUn192MSKUTF0602CdbtQvzI39HuCijTVGiEh4dWgs5BoAvnD0tfNeWrXZBOueaf_CFInJPuoR4eyAORHV5pTn8fbAEiv8mVqKvHI0OL7GvA0E1Yg7c5rlrLgJj0Ey6P5POAYCL7KOkT3kw1LKbA4glCNOiZC54XWY2V7jI1bgquxOgj0FG3Gstl9B7IMncSTuNgKZONEJ9LWWVWOtLl6kMRq0gLZqhpKUoNqkgKpiS7noUkEQJJ7n-tv1djLIfrHdwWDO2XJhb0ouXC6qgn-TKnu1eBKCaGcWvKLine truncated
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"provider": "cloudflare-ai-gateway",
|
||||
"route": "cloudflare-ai-gateway-responses",
|
||||
"transport": "http",
|
||||
"model": "gpt-5-nano",
|
||||
"tags": [
|
||||
"prefix:cloudflare-ai-gateway",
|
||||
"provider:cloudflare-ai-gateway",
|
||||
"tool",
|
||||
"tool-loop",
|
||||
"golden"
|
||||
],
|
||||
"name": "cloudflare-ai-gateway/cloudflare-ai-gateway-openai-gpt-5-nano-tool-loop",
|
||||
"recordedAt": "2026-09-30T04:36:34.984Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/openai/responses",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"gpt-5-nano\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]}],\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"max_output_tokens\":2000,\"stream\":true}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream; charset=utf-8"
|
||||
},
|
||||
"body": "event: response.created\ndata: {\"p\": \"d82f1d10b60\", \"type\":\"response.created\",\"response\":{\"id\":\"resp_06bc195765a8c9c7016abc91ce40f887d189169a2d89f2ee42\",\"object\":\"response\",\"created_at\":1790742990,\"status\":\"in_progress\",\"access_programs\":null,\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}\n\nevent: response.in_progress\ndata: {\"p\": \"0b0cb44c44\", \"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_06bc195765a8c9c7016abc91ce40f887d189169a2d89f2ee42\",\"object\":\"response\",\"created_at\":1790742990,\"status\":\"in_progress\",\"access_programs\":null,\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}\n\nevent: response.output_item.added\ndata: {\"p\": \"608c6368a0c703ea\", \"type\":\"response.output_item.added\",\"item\":{\"id\":\"rs_06bc195765a8c9c7016abc91ce881c87d18e71310b9a21b3ca\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqvJHOLa_CYgj0Ynl-_tBncVpCPOfdueKV7-3xsbVVIXhZFUdhtEfjPYHW85-TecrFUmkr7jMw4OsT2Zckq7xkxVfZ8kXNObzjU6N6t35hp8hIJ9CbGmJBsxZBQceHHtZgQrOVWCKJ7z1B1k5zsJ8lbSRkLVAahsCEwg-tYDzQAkYUcqlK7VS62sKLLfDMYpSMCvv-y-5v9I4S74P0D2Zr7znJBap72RK0tHjN-d3vdjOaxMMkLP8JlK6-HlOWabzVEPWmsdXFPFzjWuRjAOn5ILrcQzu5T8kPLjon2kW4XuwwcXeI_7UvFmt9yRfkTlVFVr-hRcSad9AjBHHYx3nmvlhM2nVuKggGolqpwdKLR87sccTmbo0kzYp8Av1yHPJ6xKxAIJYZO_XDdvXGK1vu6WFqxe37LXpWIAvJ1BbXo7yMdV2tZshqP2WBiYdILkMiZFV4VDuzuOYEx3_56QyL4sdh0p3_mxXfgjEiLNzloBrq9VtPL1YLucqzAZwCDuhtwKLC3zZ1awSC_exx7thmBbULObOBfOhWN9oIG53MrLbpJVN4MlENvtLoq7l_92k0VbPcOGDgJcmJjYOy-p7P_YDQ_Y9WR0oNpCNRFfarqGBOzegO7_UPRVUEKJeb3xx2e6-9HnrjC9jyAFs4MI54uNfKMbF7a6p55NWaEtvbTn3124XDaUD2qeSChCoAv2KFXxTIIyQeOVgVBQbHW8IamL9Eh0RvjEpg5WZqgRK4QdRZ8fiFL3lLV5stsqVvNz_1LAnGs4bnH69pmmlfO0E2C8LLhLhRVh9hcEXnKwpA6URICdn58uEPKVr9tDmTyaJbiZx_4lPbVSQ_T2ncKL8pL8gK4odQDdhe_XjdqF9i5N_t29H8A3XUDkXOgd5zyMepV1GCfKm7DaJHYXIIIjr_q4xheZFzxMl1KMQX_a9xbc-KRvOj2apHphqo5TsmYOFl5QYyt3wWllpmUZ6c9oSVOg159RFyR7UUdjGJDN8aN9pXlDjxYjR76Q9Xvmun2duw37Abi7TXl2BzJ2Bq9VFLGW_7CB_kv0vxNijP2K2XuD0Ge8tfM7HfU_gFXN-WXym5GdN6DMCxAg7whaBAj9su1wEaI11VQBTF0s41glo-V4DTcLyKTNFjScck_6a_vEnmFE57IEToGK_akhdx_SzKS9rHrA==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":2}\n\neveLine truncated
|
||||
}
|
||||
},
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/openai/responses",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"gpt-5-nano\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]},{\"type\":\"reasoning\",\"id\":\"rs_06bc195765a8c9c7016abc91ce881c87d18e71310b9a21b3ca\",\"summary\":[],\"encrypted_content\":\"gAAAAABqvJHSN68IWwhWq9YTPxs-e-OeLdOqfdkkIEP_1hc9f7k6yH9tGZKLUC2OPF0avZ7xaJQp8iOR_gPt3z0TGQ1T0e4M9OR6boMFOA_C4TwinAF1POgbZLI-0SUDkAm1PZSaO0Ic7DBh1rRT6biled_-iWs0ke3FP3nFZit5Ve16AI1TdL4c9QERNCcZFsD5neV8eBeABWarZ8SMFfxcYJ5y8QkpDOL1ShqenD22Df7fDy8cdkYWybEEZcAfgXWSfQK2hRwSNYN_BDV9CfKj-cUTePwf0D2vLm-2xd9LGt-KiLPbsp9EpGXP41ra6-qHRLeu7MO64XWIu2vsf9qAflOjp7MOSuTJ3ZvDQoTNRs-pEam8-kRYRC07Hj9IzC0sjreE1NPVzz-Yuw79gqaQokfVmuznok8XoBFKLjQ1Sa9-i8Ano4lXOKz6APnXXBdbbSUBvNV3jlmqvtxhyZ2u901SuIxZcSmPY5aAEsaHiSm2lTIuHXr6f1WZcKOsR88U4ymlI-wSGKMKJT5hh9xxvSw4VQxcbjfMBllDpH6dg3X_AUwmN6JFVWZsl1QFyaTAt3LsbUY6xfuh_IbuO30tPnG5tlUrluJdXGloRCH11NFB7wjLQ3aR1DTSxjlfce9xxfX0OdZsrIeL0Z03kveHI5ZkIjp4xQo_VAGkU-tiQ8Dyx5DYiAR-sCYaOqNwzxKWVc5YYohZKpHjpCBel0pzDgUADDWyube45tOIDVyeefNjIfmRuDnJ9ZZhi45JY4E8kRviu00sFlfVxDdKLwnykuYBEeU6RCQSx6yVr5zxErvtqEbcrz3XvNJhOiNL6n_I8D6dd4muE7j43BrQgI_ae5dakGyvy83JLyeylqZKak5GIwq7KjymQQIWMVdQIfTuF4n6HbaYLcoYZthe9WdGjo3nnBkyn08Voor8OFycbUxS6qm-siAYU_0CgG29fDgorAb9F4DmFEM_Lg0ymYyBAEXsnm9EilD0rndz-tKthJN1e53CvZZlCfhbBKxjU-hTeiBeE49YIsM7KueWdCTNetU_gSeGipB-UU-sDJMvHUR0sclIenUIMC0MSNPuUBGdzTDjQBVKmJZvKUy8NJBghXHYEzCxZRdO_qnn-FIq731ZhqA-G58_48T-tovQF3JlygWW8d-XH4PzjgSR0YK4Ucpo-d8AcY0u-2HsEv6TaxgUvBnot_kPBxFhCpdLj6nbesLCAZkvmM1erBl-ew6ztD1FSt2MIg2Nr9vqOL-cQeH9zGX0OqXrbWWqCrOJkLSoq7ST-Fq6cQhvM_6KUw_mhgWHHuYy-68EgzyzZ7K7lQAx379F2Fiv2U3SR9JCWvtja5YigRBqM7PwxIyYs_o0y4p19UZg5jTx0TyR-pVXw1_CcknqmrGginPv2zKvbRlnRm-cIEO0mWsfwUgN7DzGSKa06zoEStd-aVprHolQ4_l3oFtTUVhymo4SO5VMAWVZLrGuZNn2PRifFZlvYl00Dt1RAMG0FguNGMcLn0-0hzxio-5psReiIguJfurRHnsk3tPyYwYqILmCng1OW5ytJvRUkWYgZ_esuD_9gnHyL58mYyW5MsqFmWeMoOGeDPde9ggHqf1uAZ6NxGbpco7njgxOhPKEKkr0bMXWmn31C1wh7VM2TkwhD2M9apFLQ1eX4wHl6hZsbknl1LdZrY3pLIL_SkAL6xnfxfCcA7hGli6fnlOVhLQC4WEIR-h7zJqeK60SF9-AZt6l3LwnQru5e5aIwKWv0xqS5VLf6mOmCRC0HVAJBdYRKU2CImoih7pey6HV7-JP1nxOTiK2teo5uI2PUrMLTV_oAh-O3SFkvX8eZ0LgGDF3s-AgEq3G-Hs5qNEeX7rcyUWeTZ63HZ2w7e4R1STF0pulnJW91So2vjF9aEBFY2AkpM-rSx2kKl-xYG3UMX-57Gbw0PPTNgnhwbP1AjAlPfG3TAt6Df8ouxUYsCsnA5tZna5e4QOQW1ZcEoesgJxy24QeaJN4OUHRBlmzEQ4d20rtNJL2wsed0HXdLSU427NicPdioL1lYVHXxlnU0tQEbQbLFQzIsh2WJaFqkDsBOTrN6HXTgUfY85cDo39Rm5u6gAEnJpef8NXpeqTim5oBASsqra5hZEjYi5PkFHNcLI663eNzFLlYix73_pVIcMch6YsHg6Z8JWLo7Xhz5vN4IqxEH8Qya_odYAkNpYN_kT1oTeo4MDqs0IpvPmj6tIylYRoW3txAnatsxge0l6HGa4_tG4dP_t91rsTJTzR8wsOsQZpnrKtuYEv5WlfuvpgeITrloDcWQOxq9VeqdYFdNEJPevIwvmEIA-udNoo-Ho31ZxhbODdI9p9nOLQ9lRxBVA__VOCNJisDFVLt_PyDevENtme1qJTe-RvsCuQBhTVffUPTpc4ez7V9V8CiTN37yb2VkMtHcmxcyNYiGdwIaMe3WKS_W9bM7Fqil1LX6mVu-49CnWp7SNRHXlvCeNfFWo_DvKiu8g6aMy-th-kdRSrMjKJ_6R_5ph0GKyGkF5sirhUf6W-S5s2WnV-3yj9Y0IbNWUr8xHK6SXgOXhSLfdLcJbedHNdIXIggd-jzLcAi5F_qkRV67g1W9IKQGwTzIFvoJrlq3gChxHzyrD3gOPYiAirj_6Kh2tO8iyEtEzXPNaIWHqxPnoHC2f79AA6S1rzzt1e0IPLyo0KrCeNWPb1lUsKDKOuxPB8_v6mp1LbSxAw-qWDcgHpHrM6Hve90fw-xSyRY1ZOJ14NsJE85vw2rqSo7AJtBQC2SrIsyKy5Q7rKC2lkQeyWdpCJOEslvHPTbNTftWXs9qZKSGhHXhY2_MCR5_jetBl33KKpIqOvJZs4nkaz2tmyCbFAmfQgcreiOYA1qGNWvPNJWUifd8GZHvDiemO37ZvLew3WCloB2bsn1urkN8OGmK2-NcL3jtVr0T7fRSvGG8BETDPUDtLvg2wAQeV0kGfM6dH37ED8auPcWxygz8cDD1ni8QoPpdlHsH55kL38rEerrgI_fEcAsAGqXacW8-07vKLDt_dJ4cmSVq-DVTIw-EQIrhBOf3gW8W5LP2_sVA2mjchwWP2mZ-v-zEeYVtkzCWznJCqFbeatyxHv5Q2phnWkxj2d-lsELVDHHRXPlB-MqUBsYLAhhPJzRC9n76EoOVdgr5bsfP1XQlYj_WdPdZ13HpGZyj7ZfIPRazmYlw-EBURIYzOHlilo0Hk7pLZ5Im7E5XtLXjb0r4MaUZltViFS7Za7lm1yeVmrA0k-X4T8lgTqaBpr5BnnVVTPUNB2701N4_Z3tPxFxO3Bi_wGVVqB19D1hZGyNCXHqxlvvvPozHi3gV6Oh5ElPb4_wWTJeXJTqPI_gcnExs8Z-AnwTST6Dbw1u4EBUapdXDNcC3PD6YHlQ2Cn0lUD3TMFjGZiTL3UQADwKHEtH8R45QIEadT7O8p1pVWhzFsdOZbNdc160iViD3A920MHSRcJ_pLhxwuyndMEfzJEInv3s7lIxcJtc_VVruYr-ce24eD57jf23TsXgBEkFBsGFvORSUtds_eviuK4_wjijz33Aeydm3SqMi_DfFoy0CDSLe7qnvWBda9IF4MRlvcR-N8qgZW_vjx3OkfiUUF2PDlw1tmXhSuiF1IoPusUWZQ7QD2aEEBD_RXaipdCy_Lh7yDEw9CtdEb332MLvzFgUPBLSWWyrA6gYQwpEiJHFrQMDetsaFFL-A5188dSPLjtXi9KkxGsA5wEbA1FcvABrHOBo_Ws8V3wzIuHl7uo7PO6FGIZB-d0RZiAw7AP5cVjG0udCWmQPpS0WVF5slSNmKgZjr9WD20w9yaliZOiPN_2A2w9Zx_ca4YBceORZ69irPy0xCKQJBlq0HMeZV_WzQATzbV0n8oyfx3CB12oF9JYn_4C5vqC_QnlGmUMFno3F1ZP94Tv7XqyDTt_JTAK_xppfVJEX7018apcF2w40aM5kp6_Clv0lb7AOkGWFKOAI7qOoraTldZh6ik_1DuJqKno7k0Qtc8RnRdQjNRhXJDfb1oqRBYvBfNQkROULiOoNx2yJdp7wvMjqah7WYE1wnKbftTPLjPDQDoc=\"},{\"type\":\"function_call\",\"id\":\"fc_06bc195765a8c9c7016abc91d247ec87d19e4f16354641a20f\",\"call_id\":\"call_RN4tTgQaJ61I5FoTePK9mpHu\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"},{\"type\":\"function_call_output\",\"call_id\":\"call_RN4tTgQaJ61I5FoTePK9mpHu\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parametLine truncated
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream; charset=utf-8"
|
||||
},
|
||||
"body": "event: response.created\ndata: {\"p\": \"aa3530\", \"type\":\"response.created\",\"response\":{\"id\":\"resp_06bc195765a8c9c7016abc91d296d887d1bb460e087f742d90\",\"object\":\"response\",\"created_at\":1790742994,\"status\":\"in_progress\",\"access_programs\":null,\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}\n\nevent: response.in_progress\ndata: {\"p\": \"a6b66d35bc20b2b\", \"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_06bc195765a8c9c7016abc91d296d887d1bb460e087f742d90\",\"object\":\"response\",\"created_at\":1790742994,\"status\":\"in_progress\",\"access_programs\":null,\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"max_output_tokens\":2000,\"max_tool_calls\":null,\"model\":\"gpt-5-nano-2025-08-07\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"in_memory\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":null},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"medium\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":1.0,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}\n\nevent: response.output_item.added\ndata: {\"p\": \"169cd8bd840\", \"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}\n\nevent: response.content_part.added\ndata: {\"p\": \"045e323978cf3\", \"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}\n\nevent: response.output_text.delta\ndata: {\"p\": \"42af845ab126d6\", \"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Paris\",\"item_id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"logprobs\":[],\"obfuscation\":\"WAn5iJo53hF\",\"output_index\":0,\"sequence_number\":4}\n\nevent: response.output_text.delta\ndata: {\"p\": \"aa473d2c7c859\", \"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" is\",\"item_id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"logprobs\":[],\"obfuscation\":\"uH1AD585J0MVU\",\"output_index\":0,\"sequence_number\":5}\n\nevent: response.output_text.delta\ndata: {\"p\": \"8\", \"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" sunny\",\"item_id\":\"msg_06bc195765a8c9c7016abc91d2e9dc87d1bc4d4524c7f3037d\",\"logprobs\":[],\"obfuscaLine truncated
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"model": "openai-gpt-5-nano",
|
||||
"tags": [
|
||||
"prefix:digitalocean-chat",
|
||||
"provider:digitalocean",
|
||||
"protocol:digitalocean-chat",
|
||||
"cache",
|
||||
"tool",
|
||||
"tool-loop"
|
||||
],
|
||||
"name": "digitalocean-chat/gpt-nano-continues-a-tool-call-with-cache-markers",
|
||||
"recordedAt": "2026-10-01T07:36:41.717Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://inference.do-ai.run/v1/chat/completions",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"openai-gpt-5-nano\",\"messages\":[{\"role\":\"system\",\"content\":[{\"type\":\"text\",\"text\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"cache_control\":{\"type\":\"ephemeral\"}}]},{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false},\"cache_control\":{\"type\":\"ephemeral\"}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"store\":false,\"reasoning_effort\":\"minimal\",\"max_completion_tokens\":1024}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream"
|
||||
},
|
||||
"body": "data: {\"choices\":[{\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840200,\"id\":\"chatcmpl-EU5dIREwFebLDdrnmX7ftaDIK4qrZ\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\"\",\"name\":\"get_weather\"},\"id\":\"call_zPwN7xhG4uXQINkufgtjVJ7L\",\"index\":0,\"type\":\"function\"}]},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840200,\"id\":\"chatcmpl-EU5dIREwFebLDdrnmX7ftaDIK4qrZ\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\"{\\\"\"},\"index\":0,\"type\":\"function\"}]},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840200,\"id\":\"chatcmpl-EU5dIREwFebLDdrnmX7ftaDIK4qrZ\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\"city\"},\"index\":0,\"type\":\"function\"}]},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840200,\"id\":\"chatcmpl-EU5dIREwFebLDdrnmX7ftaDIK4qrZ\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\"\\\":\\\"\"},\"index\":0,\"type\":\"function\"}]},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840200,\"id\":\"chatcmpl-EU5dIREwFebLDdrnmX7ftaDIK4qrZ\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\"Paris\"},\"index\":0,\"type\":\"function\"}]},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840200,\"id\":\"chatcmpl-EU5dIREwFebLDdrnmX7ftaDIK4qrZ\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\"\\\"}\"},\"index\":0,\"type\":\"function\"}]},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840200,\"id\":\"chatcmpl-EU5dIREwFebLDdrnmX7ftaDIK4qrZ\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{},\"finish_reason\":\"tool_calls\",\"index\":0,\"logprobs\":null}],\"created\":1790840200,\"id\":\"chatcmpl-EU5dIREwFebLDdrnmX7ftaDIK4qrZ\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[],\"created\":1790840200,\"id\":\"chatcmpl-EU5dIREwFebLDdrnmX7ftaDIK4qrZ\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\",\"usage\":{\"cache_created_input_tokens\":0,\"cache_creation\":{\"ephemeral_1h_input_tokens\":0,\"ephemeral_5m_input_tokens\":0},\"cache_read_input_tokens\":0,\"completion_tokens\":23,\"completion_tokens_details\":{\"reasoning_tokens\":0},\"prompt_tokens\":155,\"prompt_tokens_details\":{\"cached_tokens\":0},\"speed\":null,\"total_tokens\":178}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://inference.do-ai.run/v1/chat/completions",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"openai-gpt-5-nano\",\"messages\":[{\"role\":\"system\",\"content\":[{\"type\":\"text\",\"text\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"cache_control\":{\"type\":\"ephemeral\"}}]},{\"role\":\"user\",\"content\":\"What is the weather in Paris?\"},{\"role\":\"assistant\",\"content\":null,\"tool_calls\":[{\"id\":\"call_zPwN7xhG4uXQINkufgtjVJ7L\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"}}]},{\"role\":\"tool\",\"tool_call_id\":\"call_zPwN7xhG4uXQINkufgtjVJ7L\",\"content\":[{\"type\":\"text\",\"text\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false},\"cache_control\":{\"type\":\"ephemeral\"}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"store\":false,\"reasoning_effort\":\"minimal\",\"max_completion_tokens\":1024}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream"
|
||||
},
|
||||
"body": "data: {\"choices\":[{\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840201,\"id\":\"chatcmpl-EU5dJprTkE7OHEKnmvot7rR0LSo5L\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"content\":\"Paris\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840201,\"id\":\"chatcmpl-EU5dJprTkE7OHEKnmvot7rR0LSo5L\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"content\":\" is\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840201,\"id\":\"chatcmpl-EU5dJprTkE7OHEKnmvot7rR0LSo5L\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"content\":\" sunny\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840201,\"id\":\"chatcmpl-EU5dJprTkE7OHEKnmvot7rR0LSo5L\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"content\":\".\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840201,\"id\":\"chatcmpl-EU5dJprTkE7OHEKnmvot7rR0LSo5L\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{},\"finish_reason\":\"stop\",\"index\":0,\"logprobs\":null}],\"created\":1790840201,\"id\":\"chatcmpl-EU5dJprTkE7OHEKnmvot7rR0LSo5L\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[],\"created\":1790840201,\"id\":\"chatcmpl-EU5dJprTkE7OHEKnmvot7rR0LSo5L\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\",\"usage\":{\"cache_created_input_tokens\":0,\"cache_creation\":{\"ephemeral_1h_input_tokens\":0,\"ephemeral_5m_input_tokens\":0},\"cache_read_input_tokens\":0,\"completion_tokens\":13,\"completion_tokens_details\":{\"reasoning_tokens\":0},\"prompt_tokens\":193,\"prompt_tokens_details\":{\"cached_tokens\":0},\"speed\":null,\"total_tokens\":206}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"model": "openai-gpt-5-nano",
|
||||
"tags": [
|
||||
"prefix:digitalocean-chat",
|
||||
"provider:digitalocean",
|
||||
"protocol:digitalocean-chat",
|
||||
"cache",
|
||||
"usage"
|
||||
],
|
||||
"name": "digitalocean-chat/gpt-nano-reuses-a-cached-prompt",
|
||||
"recordedAt": "2026-10-01T07:36:40.074Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://inference.do-ai.run/v1/chat/completions",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"openai-gpt-5-nano\",\"messages\":[{\"role\":\"system\",\"content\":[{\"type\":\"text\",\"text\":\"You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid fillLine truncated
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream"
|
||||
},
|
||||
"body": "data: {\"choices\":[{\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840199,\"id\":\"chatcmpl-EU5dHjjHh6g7s4uSL7kMOOpLEXVki\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"content\":\"OK\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840199,\"id\":\"chatcmpl-EU5dHjjHh6g7s4uSL7kMOOpLEXVki\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{},\"finish_reason\":\"stop\",\"index\":0,\"logprobs\":null}],\"created\":1790840199,\"id\":\"chatcmpl-EU5dHjjHh6g7s4uSL7kMOOpLEXVki\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[],\"created\":1790840199,\"id\":\"chatcmpl-EU5dHjjHh6g7s4uSL7kMOOpLEXVki\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\",\"usage\":{\"cache_created_input_tokens\":0,\"cache_creation\":{\"ephemeral_1h_input_tokens\":0,\"ephemeral_5m_input_tokens\":0},\"cache_read_input_tokens\":0,\"completion_tokens\":10,\"completion_tokens_details\":{\"reasoning_tokens\":0},\"prompt_tokens\":4765,\"prompt_tokens_details\":{\"cached_tokens\":0},\"speed\":null,\"total_tokens\":4775}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://inference.do-ai.run/v1/chat/completions",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"openai-gpt-5-nano\",\"messages\":[{\"role\":\"system\",\"content\":[{\"type\":\"text\",\"text\":\"You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid fillLine truncated
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream"
|
||||
},
|
||||
"body": "data: {\"choices\":[{\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840200,\"id\":\"chatcmpl-EU5dHYe8HWjEAEIIvsJ6Hk0EUxL9r\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"content\":\"OK\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840200,\"id\":\"chatcmpl-EU5dHYe8HWjEAEIIvsJ6Hk0EUxL9r\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{},\"finish_reason\":\"stop\",\"index\":0,\"logprobs\":null}],\"created\":1790840200,\"id\":\"chatcmpl-EU5dHYe8HWjEAEIIvsJ6Hk0EUxL9r\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[],\"created\":1790840200,\"id\":\"chatcmpl-EU5dHYe8HWjEAEIIvsJ6Hk0EUxL9r\",\"model\":\"openai-gpt-5-nano\",\"object\":\"chat.completion.chunk\",\"usage\":{\"cache_created_input_tokens\":0,\"cache_creation\":{\"ephemeral_1h_input_tokens\":0,\"ephemeral_5m_input_tokens\":0},\"cache_read_input_tokens\":4608,\"completion_tokens\":10,\"completion_tokens_details\":{\"reasoning_tokens\":0},\"prompt_tokens\":4765,\"prompt_tokens_details\":{\"cached_tokens\":4608},\"speed\":null,\"total_tokens\":4775}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"model": "anthropic-claude-haiku-4.5",
|
||||
"tags": [
|
||||
"prefix:digitalocean-chat",
|
||||
"provider:digitalocean",
|
||||
"protocol:digitalocean-chat",
|
||||
"cache",
|
||||
"tool",
|
||||
"tool-loop"
|
||||
],
|
||||
"name": "digitalocean-chat/haiku-continues-a-tool-call-with-cache-markers",
|
||||
"recordedAt": "2026-10-01T07:36:38.471Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://inference.do-ai.run/v1/chat/completions",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"anthropic-claude-haiku-4.5\",\"messages\":[{\"role\":\"system\",\"content\":[{\"type\":\"text\",\"text\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"cache_control\":{\"type\":\"ephemeral\"}}]},{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"What is the weather in Paris?\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false},\"cache_control\":{\"type\":\"ephemeral\"}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"store\":false,\"max_completion_tokens\":128}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream"
|
||||
},
|
||||
"body": "data: {\"choices\":[{\"delta\":{\"content\":\"\",\"role\":\"assistant\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840197,\"id\":\"chatcmpl-ccddd771-e8a6-4b59-b819-614f79ac4624\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\"\",\"name\":\"get_weather\"},\"id\":\"toolu_014LEa2F66CGw2SoLmWBeH5C\",\"index\":0,\"type\":\"function\"}]},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840197,\"id\":\"chatcmpl-ccddd771-e8a6-4b59-b819-614f79ac4624\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\"\"},\"index\":0,\"type\":\"function\"}]},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840197,\"id\":\"chatcmpl-ccddd771-e8a6-4b59-b819-614f79ac4624\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\"{\\\"city\\\"\"},\"index\":0,\"type\":\"function\"}]},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840197,\"id\":\"chatcmpl-ccddd771-e8a6-4b59-b819-614f79ac4624\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\": \\\"Pari\"},\"index\":0,\"type\":\"function\"}]},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840197,\"id\":\"chatcmpl-ccddd771-e8a6-4b59-b819-614f79ac4624\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"function\":{\"arguments\":\"s\\\"}\"},\"index\":0,\"type\":\"function\"}]},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840197,\"id\":\"chatcmpl-ccddd771-e8a6-4b59-b819-614f79ac4624\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{},\"finish_reason\":\"tool_calls\",\"index\":0,\"logprobs\":null}],\"created\":1790840197,\"id\":\"chatcmpl-ccddd771-e8a6-4b59-b819-614f79ac4624\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[],\"created\":1790840197,\"id\":\"chatcmpl-ccddd771-e8a6-4b59-b819-614f79ac4624\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\",\"usage\":{\"cache_created_input_tokens\":0,\"cache_creation\":{\"ephemeral_1h_input_tokens\":0,\"ephemeral_5m_input_tokens\":0},\"cache_read_input_tokens\":0,\"completion_tokens\":54,\"completion_tokens_details\":{\"reasoning_tokens\":0},\"prompt_tokens\":593,\"prompt_tokens_details\":{\"cached_tokens\":0},\"speed\":null,\"total_tokens\":647}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://inference.do-ai.run/v1/chat/completions",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"anthropic-claude-haiku-4.5\",\"messages\":[{\"role\":\"system\",\"content\":[{\"type\":\"text\",\"text\":\"Use the get_weather tool exactly once. After the tool result, reply exactly: Paris is sunny.\",\"cache_control\":{\"type\":\"ephemeral\"}}]},{\"role\":\"user\",\"content\":\"What is the weather in Paris?\"},{\"role\":\"assistant\",\"content\":null,\"tool_calls\":[{\"id\":\"toolu_014LEa2F66CGw2SoLmWBeH5C\",\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"}}]},{\"role\":\"tool\",\"tool_call_id\":\"toolu_014LEa2F66CGw2SoLmWBeH5C\",\"content\":[{\"type\":\"text\",\"text\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\",\"cache_control\":{\"type\":\"ephemeral\"}}]}],\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false},\"cache_control\":{\"type\":\"ephemeral\"}}],\"stream\":true,\"stream_options\":{\"include_usage\":true},\"store\":false,\"max_completion_tokens\":128}"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream"
|
||||
},
|
||||
"body": "data: {\"choices\":[{\"delta\":{\"content\":\"Paris\",\"role\":\"assistant\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840198,\"id\":\"chatcmpl-3914ee28-f256-4a49-994f-49365922a7a6\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{\"content\":\" is sunny.\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840198,\"id\":\"chatcmpl-3914ee28-f256-4a49-994f-49365922a7a6\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{},\"finish_reason\":\"stop\",\"index\":0,\"logprobs\":null}],\"created\":1790840198,\"id\":\"chatcmpl-3914ee28-f256-4a49-994f-49365922a7a6\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[],\"created\":1790840198,\"id\":\"chatcmpl-3914ee28-f256-4a49-994f-49365922a7a6\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\",\"usage\":{\"cache_created_input_tokens\":0,\"cache_creation\":{\"ephemeral_1h_input_tokens\":0,\"ephemeral_5m_input_tokens\":0},\"cache_read_input_tokens\":0,\"completion_tokens\":7,\"completion_tokens_details\":{\"reasoning_tokens\":0},\"prompt_tokens\":668,\"prompt_tokens_details\":{\"cached_tokens\":0},\"speed\":null,\"total_tokens\":675}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"model": "anthropic-claude-haiku-4.5",
|
||||
"tags": [
|
||||
"prefix:digitalocean-chat",
|
||||
"provider:digitalocean",
|
||||
"protocol:digitalocean-chat",
|
||||
"cache",
|
||||
"usage"
|
||||
],
|
||||
"name": "digitalocean-chat/haiku-reuses-a-cached-prompt",
|
||||
"recordedAt": "2026-10-01T07:36:36.689Z"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://inference.do-ai.run/v1/chat/completions",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"anthropic-claude-haiku-4.5\",\"messages\":[{\"role\":\"system\",\"content\":[{\"type\":\"text\",\"text\":\"You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and aLine truncated
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream"
|
||||
},
|
||||
"body": "data: {\"choices\":[{\"delta\":{\"content\":\"OK\",\"role\":\"assistant\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840195,\"id\":\"chatcmpl-94f208e5-82fa-4ec6-b014-1b4ea9fbb98c\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{},\"finish_reason\":\"stop\",\"index\":0,\"logprobs\":null}],\"created\":1790840195,\"id\":\"chatcmpl-94f208e5-82fa-4ec6-b014-1b4ea9fbb98c\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[],\"created\":1790840195,\"id\":\"chatcmpl-94f208e5-82fa-4ec6-b014-1b4ea9fbb98c\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\",\"usage\":{\"cache_created_input_tokens\":5759,\"cache_creation\":{\"ephemeral_1h_input_tokens\":0,\"ephemeral_5m_input_tokens\":5759},\"cache_read_input_tokens\":0,\"completion_tokens\":4,\"completion_tokens_details\":{\"reasoning_tokens\":0},\"prompt_tokens\":5762,\"prompt_tokens_details\":{\"cached_tokens\":0},\"speed\":null,\"total_tokens\":5766}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"transport": "http",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "https://inference.do-ai.run/v1/chat/completions",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": "{\"model\":\"anthropic-claude-haiku-4.5\",\"messages\":[{\"role\":\"system\",\"content\":[{\"type\":\"text\",\"text\":\"You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and avoid filler. Cite numbers when known. You are a concise, factual assistant. Answer precisely and aLine truncated
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream"
|
||||
},
|
||||
"body": "data: {\"choices\":[{\"delta\":{\"content\":\"OK\",\"role\":\"assistant\"},\"finish_reason\":null,\"index\":0,\"logprobs\":null}],\"created\":1790840196,\"id\":\"chatcmpl-eee2d578-6fab-4fc1-8117-043193225bed\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[{\"delta\":{},\"finish_reason\":\"stop\",\"index\":0,\"logprobs\":null}],\"created\":1790840196,\"id\":\"chatcmpl-eee2d578-6fab-4fc1-8117-043193225bed\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\"}\n\ndata: {\"choices\":[],\"created\":1790840196,\"id\":\"chatcmpl-eee2d578-6fab-4fc1-8117-043193225bed\",\"model\":\"anthropic-claude-haiku-4.5\",\"object\":\"chat.completion.chunk\",\"usage\":{\"cache_created_input_tokens\":0,\"cache_creation\":{\"ephemeral_1h_input_tokens\":0,\"ephemeral_5m_input_tokens\":0},\"cache_read_input_tokens\":5759,\"completion_tokens\":4,\"completion_tokens_details\":{\"reasoning_tokens\":0},\"prompt_tokens\":5762,\"prompt_tokens_details\":{\"cached_tokens\":5759},\"speed\":null,\"total_tokens\":5766}}\n\ndata: [DONE]\n\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
import { describe, expect } from "bun:test"
|
||||
import { Deferred, Effect, Fiber } from "effect"
|
||||
import * as TestClock from "effect/testing/TestClock"
|
||||
import { HttpOptions, LLM, mergeHttpOptions } from "../src/index.js"
|
||||
import { LLMClient } from "../src/route.js"
|
||||
import { configure } from "../src/providers/openai.js"
|
||||
import { dynamicResponse } from "./lib/http.js"
|
||||
import { deltaChunk, finishChunk } from "./lib/openai-chunks.js"
|
||||
import { sseEvents, sseRaw } from "./lib/sse.js"
|
||||
import { it } from "./lib/effect.js"
|
||||
|
||||
const model = configure({ baseURL: "https://api.openai.test/v1/", apiKey: "test" }).chat("gpt-4.1-mini")
|
||||
const SSE = { headers: { "content-type": "text/event-stream" } }
|
||||
|
||||
const silentServer = dynamicResponse(() => Effect.never)
|
||||
|
||||
const slowHeadersServer = dynamicResponse((input) =>
|
||||
Effect.sleep("10 minutes").pipe(
|
||||
Effect.as(input.respond(sseEvents(deltaChunk({ role: "assistant", content: "Hi" }), finishChunk("stop")), SSE)),
|
||||
),
|
||||
)
|
||||
|
||||
// Sends one chunk, then stalls until `resume` releases the rest of the body.
|
||||
const stalledServer = Effect.gen(function* () {
|
||||
const stalled = yield* Deferred.make<void>()
|
||||
let resume = () => {}
|
||||
const released = new Promise<void>((resolve) => {
|
||||
resume = resolve
|
||||
})
|
||||
const encoder = new TextEncoder()
|
||||
const layer = dynamicResponse((input) =>
|
||||
Effect.sync(() =>
|
||||
input.respond(
|
||||
new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
controller.enqueue(encoder.encode(sseRaw(`data: ${JSON.stringify(deltaChunk({ content: "Hi" }))}`)))
|
||||
},
|
||||
async pull(controller) {
|
||||
Deferred.doneUnsafe(stalled, Effect.void)
|
||||
await released
|
||||
controller.enqueue(encoder.encode(sseEvents(finishChunk("stop"))))
|
||||
controller.close()
|
||||
},
|
||||
}),
|
||||
SSE,
|
||||
),
|
||||
),
|
||||
)
|
||||
return { layer, stalled, resume: () => resume() }
|
||||
})
|
||||
|
||||
describe("HTTP transport timeouts", () => {
|
||||
it.effect("fails when response headers take longer than five minutes", () =>
|
||||
Effect.gen(function* () {
|
||||
const fiber = yield* LLMClient.generate(LLM.request({ model, prompt: "Hello" })).pipe(
|
||||
Effect.provide(silentServer),
|
||||
Effect.flip,
|
||||
Effect.forkChild({ startImmediately: true }),
|
||||
)
|
||||
yield* TestClock.adjust("5 minutes")
|
||||
const error = yield* Fiber.join(fiber)
|
||||
|
||||
expect(error.reason).toMatchObject({
|
||||
_tag: "Transport",
|
||||
transport: "http",
|
||||
operation: "request",
|
||||
code: "Timeout",
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("fails when the response body stalls for five minutes", () =>
|
||||
Effect.gen(function* () {
|
||||
const server = yield* stalledServer
|
||||
const fiber = yield* LLMClient.generate(LLM.request({ model, prompt: "Hello" })).pipe(
|
||||
Effect.provide(server.layer),
|
||||
Effect.flip,
|
||||
Effect.forkChild({ startImmediately: true }),
|
||||
)
|
||||
yield* Deferred.await(server.stalled)
|
||||
yield* Effect.yieldNow
|
||||
yield* TestClock.adjust("5 minutes")
|
||||
const error = yield* Fiber.join(fiber)
|
||||
|
||||
expect(error.reason).toMatchObject({ _tag: "Transport", transport: "http", operation: "read", code: "Timeout" })
|
||||
expect(error.reason.http).toMatchObject({ status: 200 })
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("applies a configured header timeout", () =>
|
||||
Effect.gen(function* () {
|
||||
const fiber = yield* LLMClient.generate(
|
||||
LLM.request({ model, prompt: "Hello", http: { headerTimeout: 1_000 } }),
|
||||
).pipe(Effect.provide(silentServer), Effect.flip, Effect.forkChild({ startImmediately: true }))
|
||||
yield* TestClock.adjust("1 second")
|
||||
const error = yield* Fiber.join(fiber)
|
||||
|
||||
expect(error.reason).toMatchObject({ _tag: "Transport", operation: "request", code: "Timeout" })
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("disables the header timeout with false", () =>
|
||||
Effect.gen(function* () {
|
||||
const fiber = yield* LLMClient.generate(
|
||||
LLM.request({ model, prompt: "Hello", http: { headerTimeout: false } }),
|
||||
).pipe(Effect.provide(slowHeadersServer), Effect.forkChild({ startImmediately: true }))
|
||||
yield* TestClock.adjust("10 minutes")
|
||||
const response = yield* Fiber.join(fiber)
|
||||
|
||||
expect(response.text).toBe("Hi")
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("disables the chunk timeout with false", () =>
|
||||
Effect.gen(function* () {
|
||||
const server = yield* stalledServer
|
||||
const fiber = yield* LLMClient.generate(
|
||||
LLM.request({ model, prompt: "Hello", http: { chunkTimeout: false } }),
|
||||
).pipe(Effect.provide(server.layer), Effect.forkChild({ startImmediately: true }))
|
||||
yield* Deferred.await(server.stalled)
|
||||
yield* Effect.yieldNow
|
||||
yield* TestClock.adjust("10 minutes")
|
||||
server.resume()
|
||||
const response = yield* Fiber.join(fiber)
|
||||
|
||||
expect(response.text).toBe("Hi")
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("applies a whole-request timeout while the body is still streaming", () =>
|
||||
Effect.gen(function* () {
|
||||
const server = yield* stalledServer
|
||||
const fiber = yield* LLMClient.generate(
|
||||
LLM.request({ model, prompt: "Hello", http: { timeout: 60_000, chunkTimeout: false } }),
|
||||
).pipe(Effect.provide(server.layer), Effect.flip, Effect.forkChild({ startImmediately: true }))
|
||||
yield* Deferred.await(server.stalled)
|
||||
yield* Effect.yieldNow
|
||||
yield* TestClock.adjust("1 minute")
|
||||
const error = yield* Fiber.join(fiber)
|
||||
|
||||
expect(error.reason).toMatchObject({ _tag: "Transport", operation: "read", code: "Timeout" })
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("bounds the header wait by a shorter whole-request timeout", () =>
|
||||
Effect.gen(function* () {
|
||||
const fiber = yield* LLMClient.generate(
|
||||
LLM.request({ model, prompt: "Hello", http: { timeout: 1_000, headerTimeout: false } }),
|
||||
).pipe(Effect.provide(silentServer), Effect.flip, Effect.forkChild({ startImmediately: true }))
|
||||
yield* TestClock.adjust("1 second")
|
||||
const error = yield* Fiber.join(fiber)
|
||||
|
||||
expect(error.reason).toMatchObject({ _tag: "Transport", operation: "request", code: "Timeout" })
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("merges timeouts with later values winning", () =>
|
||||
Effect.sync(() => {
|
||||
const merged = mergeHttpOptions(
|
||||
new HttpOptions({ timeout: 5_000, headerTimeout: 1_000, chunkTimeout: 2_000 }),
|
||||
new HttpOptions({ headers: { a: "b" } }),
|
||||
new HttpOptions({ chunkTimeout: false }),
|
||||
)
|
||||
|
||||
expect(merged).toMatchObject({ headers: { a: "b" }, timeout: 5_000, headerTimeout: 1_000, chunkTimeout: false })
|
||||
expect(mergeHttpOptions(new HttpOptions({}), undefined)).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
})
|
||||
@@ -7,12 +7,15 @@ describe("provider error classification", () => {
|
||||
const messages = [
|
||||
"tokens in request more than max tokens allowed",
|
||||
"Requested token count exceeds the model's maximum context length of 131072 tokens.",
|
||||
"Requested input length 600010 exceeds maximum input length 131071",
|
||||
"Input length (265330) exceeds model's maximum context length (262144).",
|
||||
"Input length 131393 exceeds the maximum allowed input length of 131040 tokens.",
|
||||
"The input (516368 tokens) is longer than the model's context length (262144 tokens).",
|
||||
"The input is longer than the model's context length trace_id: 39d8c3a5c6c91dbc8dd9055f0b37e084",
|
||||
"Prompt has 5,958,968 tokens, but the configured context size is 256,000 tokens",
|
||||
"Range of input length should be [1, 129024]",
|
||||
"Too many tokens",
|
||||
"Input validation error: `inputs` tokens + `max_new_tokens` must be <= 131073. Given: 600035 `inputs` tokens and 16 `max_new_tokens`",
|
||||
"Token limit exceeded",
|
||||
]
|
||||
|
||||
@@ -282,6 +285,82 @@ describe("provider error classification", () => {
|
||||
).toEqual(Array(6).fill("QuotaExceeded"))
|
||||
})
|
||||
|
||||
test("classifies Z.ai plan and balance limits as quota rather than throttling", () => {
|
||||
const zai = (code: string, message: string) => ({ error: { code, message } })
|
||||
const cases = [
|
||||
zai("1113", "Insufficient balance or no resource package. Please recharge."),
|
||||
zai("1308", "Usage limit reached for 5 hours. Your limit will reset at 2026-10-01 00:00:00"),
|
||||
zai(
|
||||
"1309",
|
||||
"Your GLM Coding Plan package has expired and is temporarily unavailable. You can resume using it after renewing the subscription on the official website.",
|
||||
),
|
||||
zai("1310", "Weekly/Monthly Limit Exhausted. Your limit will reset at 2026-10-01 00:00:00"),
|
||||
zai("1311", "Your current subscription plan does not yet include access to glm-5"),
|
||||
zai("1314", "Your enterprise package has expired. Please contact your enterprise administrator."),
|
||||
// Z.ai's Anthropic-compatible endpoint wraps the code and request ID into the message.
|
||||
{
|
||||
type: "error",
|
||||
error: {
|
||||
type: "rate_limit_error",
|
||||
code: "1309",
|
||||
message:
|
||||
"[1309][Your GLM Coding Plan package has expired and is temporarily unavailable. You can resume using it after renewing the subscription on the official website.][20260929132151e73af01340d54b58]",
|
||||
},
|
||||
},
|
||||
]
|
||||
expect(
|
||||
cases.map(
|
||||
(body) =>
|
||||
classifyProviderFailure({ message: body.error.message, status: 429, rawBody: JSON.stringify(body) })._tag,
|
||||
),
|
||||
).toEqual(Array(cases.length).fill("QuotaExceeded"))
|
||||
})
|
||||
|
||||
test("classifies Z.ai prompt length rejections as context overflow", () => {
|
||||
const cases = [
|
||||
{ error: { code: "1261", message: "Prompt 超长" } },
|
||||
{ error: { code: "1261", message: "Prompt too long" } },
|
||||
{
|
||||
type: "error",
|
||||
error: { type: "invalid_request_error", code: "1261", message: "[1261][Prompt too long][2026092913]" },
|
||||
},
|
||||
]
|
||||
expect(
|
||||
cases.map((body) => {
|
||||
const reason = classifyProviderFailure({
|
||||
message: body.error.message,
|
||||
status: 400,
|
||||
rawBody: JSON.stringify(body),
|
||||
})
|
||||
return reason._tag === "InvalidRequest" ? reason.classification : reason._tag
|
||||
}),
|
||||
).toEqual(["context-overflow", "context-overflow", "context-overflow"])
|
||||
})
|
||||
|
||||
test("classifies Z.ai sensitive content rejections as content policy", () => {
|
||||
const message =
|
||||
"System detected potentially unsafe or sensitive content in input or generation. Please avoid using prompts that may generate sensitive content. Thank you for your cooperation."
|
||||
expect(
|
||||
classifyProviderFailure({
|
||||
message,
|
||||
status: 400,
|
||||
rawBody: JSON.stringify({ error: { code: "1301", message } }),
|
||||
})._tag,
|
||||
).toBe("ContentPolicy")
|
||||
})
|
||||
|
||||
test("keeps Z.ai throttling and overload retryable", () => {
|
||||
expect(
|
||||
[
|
||||
{ error: { code: "1302", message: "Rate limit reached for requests" } },
|
||||
{ error: { code: "1305", message: "The service may be temporarily overloaded, please try again later" } },
|
||||
].map(
|
||||
(body) =>
|
||||
classifyProviderFailure({ message: body.error.message, status: 429, rawBody: JSON.stringify(body) })._tag,
|
||||
),
|
||||
).toEqual(["RateLimit", "RateLimit"])
|
||||
})
|
||||
|
||||
test("does not let substituted server codes make a 4xx retryable", () => {
|
||||
const openai = { error: { type: "server_error", message: "Upstream request failed: Model is unavailable." } }
|
||||
const anthropic = {
|
||||
@@ -366,7 +445,24 @@ describe("provider error rawBody classification", () => {
|
||||
expect(reason._tag === "InvalidRequest" ? reason.classification : reason._tag).toBe("context-overflow")
|
||||
})
|
||||
|
||||
test("classifies Google invalid API keys as authentication failures", () => {
|
||||
test("classifies Amazon Nova input token rejections as context overflow", () => {
|
||||
const message =
|
||||
"The model returned the following errors: Input Tokens Exceeded: Number of input tokens exceeds maximum length. Please update the input to try again."
|
||||
const reason = classifyProviderFailure({
|
||||
message,
|
||||
rawBody: JSON.stringify({
|
||||
headers: {
|
||||
":exception-type": { type: "string", value: "validationException" },
|
||||
":content-type": { type: "string", value: "application/json" },
|
||||
":message-type": { type: "string", value: "exception" },
|
||||
},
|
||||
body: JSON.stringify({ message }),
|
||||
}),
|
||||
})
|
||||
expect(reason._tag === "InvalidRequest" ? reason.classification : reason._tag).toBe("context-overflow")
|
||||
})
|
||||
|
||||
test("classifies invalid API keys reported as HTTP 400 as authentication failures", () => {
|
||||
const rawBody = JSON.stringify({
|
||||
error: {
|
||||
code: 400,
|
||||
@@ -390,6 +486,14 @@ describe("provider error rawBody classification", () => {
|
||||
classifyProviderFailure({ message: "API key not valid. Please pass a valid API key.", status: 400, rawBody })
|
||||
._tag,
|
||||
).toBe("Authentication")
|
||||
// xAI
|
||||
expect(
|
||||
classifyProviderFailure({
|
||||
message: "Incorrect API key provided. You can obtain an API key from https://console.x.ai.",
|
||||
status: 400,
|
||||
rawBody: '{"code":"invalid-argument","error":"Incorrect API key provided."}',
|
||||
})._tag,
|
||||
).toBe("Authentication")
|
||||
})
|
||||
|
||||
test("classifies overflow signals buried in the raw payload when the summary is vague", () => {
|
||||
@@ -405,6 +509,19 @@ describe("provider error rawBody classification", () => {
|
||||
expect(
|
||||
classifyProviderFailure({ message: "Request failed", rawBody: '{"error":{"code":"insufficient_quota"}}' })._tag,
|
||||
).toBe("QuotaExceeded")
|
||||
// Z.ai Responses stream rejections
|
||||
expect(
|
||||
classifyProviderFailure({
|
||||
message: "Unknown Model, please check the model code.",
|
||||
rawBody: '{"type":"response.failed","response":{"error":{"code":"model_not_found"}}}',
|
||||
})._tag,
|
||||
).toBe("InvalidRequest")
|
||||
expect(
|
||||
classifyProviderFailure({
|
||||
message: "Your GLM Coding Plan package has expired and is temporarily unavailable.",
|
||||
rawBody: '{"type":"response.failed","response":{"error":{"code":"permission_denied"}}}',
|
||||
})._tag,
|
||||
).toBe("Authentication")
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ describe("provider package entrypoints", () => {
|
||||
})
|
||||
expect(selected.provider).toBe(provider.id)
|
||||
expect(selected.route.endpoint.baseURL).toBe(provider.baseURL({ accountId: "account" }))
|
||||
expect(selected.route.defaults.headers).toEqual({ "x-application": "opencode" })
|
||||
expect(selected.route.defaults.headers).toMatchObject({ "x-application": "opencode" })
|
||||
expect(selected.route.defaults.http?.body).toEqual({ custom: true })
|
||||
expect(selected.route.defaults.providerOptions).toEqual({ reasoningEffort: "high" })
|
||||
}
|
||||
|
||||
@@ -314,6 +314,9 @@ describe("Anthropic Messages route", () => {
|
||||
"claude-haiku-5-1",
|
||||
"claude-fable-6",
|
||||
"anthropic/claude-mythos-7.2",
|
||||
"claude-sonnet-5-5",
|
||||
"claude-opus-4-8@20260101",
|
||||
"claude-nova-6",
|
||||
]
|
||||
|
||||
const prepared = yield* Effect.forEach(ids, (id) =>
|
||||
@@ -588,9 +591,9 @@ describe("Anthropic Messages route", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("rejects a system update between a local tool call and its result", () =>
|
||||
it.effect("moves a system update between a local tool call and its result after the result", () =>
|
||||
Effect.gen(function* () {
|
||||
const error = yield* compileRequest(
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({
|
||||
model: opus48,
|
||||
messages: [
|
||||
@@ -601,9 +604,13 @@ describe("Anthropic Messages route", () => {
|
||||
],
|
||||
cache: "none",
|
||||
}),
|
||||
).pipe(Effect.flip)
|
||||
)
|
||||
|
||||
expect(error.message).toContain("system updates cannot split a local tool call from its tool result")
|
||||
expect(prepared.body.messages.slice(1)).toEqual([
|
||||
{ role: "assistant", content: [{ type: "tool_use", id: "call_1", name: "lookup", input: {} }] },
|
||||
{ role: "user", content: [{ type: "tool_result", tool_use_id: "call_1", content: '"Done."' }] },
|
||||
{ role: "system", content: [{ type: "text", text: "Too early.", cache_control: undefined }] },
|
||||
])
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -1669,6 +1676,40 @@ describe("Anthropic Messages route", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("carries a refusal's category and explanation on the content-filter finish", () =>
|
||||
Effect.gen(function* () {
|
||||
const refusal = (stop_details: unknown) =>
|
||||
LLMClient.generate(request).pipe(
|
||||
Effect.provide(
|
||||
fixedResponse(
|
||||
sseEvents(
|
||||
{ type: "message_start", message: { usage: { input_tokens: 5 } } },
|
||||
{ type: "message_delta", delta: { stop_reason: "refusal", stop_details }, usage: { output_tokens: 0 } },
|
||||
{ type: "message_stop" },
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
expect(
|
||||
(yield* refusal({
|
||||
type: "refusal",
|
||||
category: "cyber",
|
||||
explanation: "This request was declined because it could enable cyber harm.",
|
||||
})).finishReason,
|
||||
).toEqual({
|
||||
normalized: "content-filter",
|
||||
raw: "refusal",
|
||||
category: "cyber",
|
||||
explanation: "This request was declined because it could enable cyber harm.",
|
||||
})
|
||||
expect((yield* refusal({ type: "refusal", category: null, explanation: null })).finishReason).toEqual({
|
||||
normalized: "content-filter",
|
||||
raw: "refusal",
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("assembles streamed tool call input", () =>
|
||||
Effect.gen(function* () {
|
||||
const body = sseEvents(
|
||||
|
||||
@@ -501,25 +501,23 @@ describe("Bedrock Converse route", () => {
|
||||
LLM.request({ model, messages: [Message.assistant([call])], cache: "none" }),
|
||||
)
|
||||
|
||||
expect(prepared.body.messages).toEqual([
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
toolUse: {
|
||||
toolUseId: "tool_1",
|
||||
name: "edit",
|
||||
input: {
|
||||
path: "file.ts",
|
||||
edits: [{ oldText: "a", newText: "b" }, null, true, 7, "text", ["kept", { nested: { value: "ok" } }]],
|
||||
nested: { empty: {}, onlyEmpty: {} },
|
||||
" ": "preserve whitespace key",
|
||||
},
|
||||
expect(prepared.body.messages[0]).toEqual({
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
toolUse: {
|
||||
toolUseId: "tool_1",
|
||||
name: "edit",
|
||||
input: {
|
||||
path: "file.ts",
|
||||
edits: [{ oldText: "a", newText: "b" }, null, true, 7, "text", ["kept", { nested: { value: "ok" } }]],
|
||||
nested: { empty: {}, onlyEmpty: {} },
|
||||
" ": "preserve whitespace key",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
},
|
||||
],
|
||||
})
|
||||
expect(input).toEqual(original)
|
||||
expect(call.input).toBe(input)
|
||||
}),
|
||||
@@ -540,15 +538,13 @@ describe("Bedrock Converse route", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
expect(prepared.body.messages).toEqual([
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ toolUse: { toolUseId: "tool_empty_key", name: "first", input: {} } },
|
||||
{ toolUse: { toolUseId: "tool_empty_object", name: "second", input: {} } },
|
||||
],
|
||||
},
|
||||
])
|
||||
expect(prepared.body.messages[0]).toEqual({
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ toolUse: { toolUseId: "tool_empty_key", name: "first", input: {} } },
|
||||
{ toolUse: { toolUseId: "tool_empty_object", name: "second", input: {} } },
|
||||
],
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -652,6 +648,177 @@ describe("Bedrock Converse route", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("hoists tool-result images beside the result for Bedrock GPT models", () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({
|
||||
model: AmazonBedrock.configure({ baseURL: "https://bedrock-runtime.test", apiKey: "test-bearer" }).model(
|
||||
"global.openai.gpt-6-sol",
|
||||
),
|
||||
messages: [
|
||||
Message.user("What is in this image?"),
|
||||
Message.assistant([ToolCallPart.make({ id: "tool_1", name: "read", input: {} })]),
|
||||
Message.tool({
|
||||
id: "tool_1",
|
||||
name: "read",
|
||||
result: {
|
||||
type: "content",
|
||||
value: [
|
||||
{ type: "text", text: "Image loaded." },
|
||||
{ type: "file", uri: "data:image/png;base64,AAAA", mime: "image/png" },
|
||||
{ type: "file", uri: "data:application/pdf;base64,QkI=", mime: "application/pdf", name: "note.pdf" },
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
cache: "none",
|
||||
}),
|
||||
)
|
||||
|
||||
expect(prepared.body.messages[2]).toEqual({
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
toolResult: {
|
||||
toolUseId: "tool_1",
|
||||
content: [
|
||||
{ text: "Image loaded." },
|
||||
{ text: 'Attached file "note.pdf" has document label "note".' },
|
||||
{ document: { format: "pdf", name: "note", source: { bytes: "QkI=" } } },
|
||||
],
|
||||
status: "success",
|
||||
},
|
||||
},
|
||||
{ image: { format: "png", source: { bytes: "AAAA" } } },
|
||||
],
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("hoists tool-result images for other Bedrock model families", () =>
|
||||
Effect.gen(function* () {
|
||||
for (const id of [
|
||||
"qwen.qwen3-vl-235b-a22b",
|
||||
"global.xai.grok-4.7",
|
||||
"global.moonshotai.kimi-k3",
|
||||
"us.meta.llama4-scout-17b-instruct-v1:0",
|
||||
]) {
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({
|
||||
model: AmazonBedrock.configure({ baseURL: "https://bedrock-runtime.test", apiKey: "test-bearer" }).model(
|
||||
id,
|
||||
),
|
||||
messages: [
|
||||
Message.assistant([ToolCallPart.make({ id: "tool_1", name: "read", input: {} })]),
|
||||
Message.tool({
|
||||
id: "tool_1",
|
||||
name: "read",
|
||||
result: {
|
||||
type: "content",
|
||||
value: [{ type: "file", uri: "data:image/png;base64,AAAA", mime: "image/png" }],
|
||||
},
|
||||
}),
|
||||
],
|
||||
cache: "none",
|
||||
}),
|
||||
)
|
||||
expect(prepared.body.messages[1]).toEqual({
|
||||
role: "user",
|
||||
content: [
|
||||
{ toolResult: { toolUseId: "tool_1", content: [{ text: "See attached image." }], status: "success" } },
|
||||
{ image: { format: "png", source: { bytes: "AAAA" } } },
|
||||
],
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
;["global.anthropic.claude-sonnet-4-5-20250929-v1:0", "us.amazon.nova-pro-v1:0"].forEach((id) => {
|
||||
it.effect(`keeps ${id} tool images inside the result`, () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({
|
||||
model: AmazonBedrock.configure({ baseURL: "https://bedrock-runtime.test", apiKey: "test-bearer" }).model(
|
||||
id,
|
||||
),
|
||||
messages: [
|
||||
Message.assistant([ToolCallPart.make({ id: "tool_1", name: "read", input: {} })]),
|
||||
Message.tool({
|
||||
id: "tool_1",
|
||||
name: "read",
|
||||
result: {
|
||||
type: "content",
|
||||
value: [{ type: "file", uri: "data:image/png;base64,AAAA", mime: "image/png" }],
|
||||
},
|
||||
}),
|
||||
],
|
||||
cache: "none",
|
||||
}),
|
||||
)
|
||||
|
||||
expect(prepared.body.messages[1]).toEqual({
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
toolResult: {
|
||||
toolUseId: "tool_1",
|
||||
content: [{ image: { format: "png", source: { bytes: "AAAA" } } }],
|
||||
status: "success",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it.effect("keeps parallel tool results before hoisted images and gives image-only results text", () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({
|
||||
model: AmazonBedrock.configure({ baseURL: "https://bedrock-runtime.test", apiKey: "test-bearer" }).model(
|
||||
"global.openai.gpt-6-sol",
|
||||
),
|
||||
messages: [
|
||||
Message.assistant([
|
||||
ToolCallPart.make({ id: "tool_1", name: "first", input: {} }),
|
||||
ToolCallPart.make({ id: "tool_2", name: "second", input: {} }),
|
||||
]),
|
||||
Message.tool({
|
||||
id: "tool_1",
|
||||
name: "first",
|
||||
result: {
|
||||
type: "content",
|
||||
value: [{ type: "file", uri: "data:image/png;base64,AAAA", mime: "image/png" }],
|
||||
},
|
||||
}),
|
||||
Message.tool({
|
||||
id: "tool_2",
|
||||
name: "second",
|
||||
result: {
|
||||
type: "content",
|
||||
value: [
|
||||
{ type: "text", text: "Second image." },
|
||||
{ type: "file", uri: "data:image/jpeg;base64,BBBB", mime: "image/jpeg" },
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
cache: "none",
|
||||
}),
|
||||
)
|
||||
|
||||
expect(prepared.body.messages[1]).toEqual({
|
||||
role: "user",
|
||||
content: [
|
||||
{ toolResult: { toolUseId: "tool_1", content: [{ text: "See attached image." }], status: "success" } },
|
||||
{ toolResult: { toolUseId: "tool_2", content: [{ text: "Second image." }], status: "success" } },
|
||||
{ image: { format: "png", source: { bytes: "AAAA" } } },
|
||||
{ image: { format: "jpeg", source: { bytes: "BBBB" } } },
|
||||
],
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("decodes text-delta + messageStop + metadata usage from binary event stream", () =>
|
||||
Effect.gen(function* () {
|
||||
const body = eventStreamBody(
|
||||
@@ -1099,6 +1266,12 @@ describe("Bedrock Converse route", () => {
|
||||
{ toolUse: { toolUseId: "call_1", name: "lookup", input: {} } },
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{ toolResult: { toolUseId: "call_1", content: [{ text: "Tool result missing" }], status: "error" } },
|
||||
],
|
||||
},
|
||||
])
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -39,9 +39,16 @@ describe("Cloudflare", () => {
|
||||
)
|
||||
|
||||
expect(responses.route).toBe("cloudflare-ai-gateway-responses")
|
||||
expect(responses.body).toMatchObject({ model: "openai/gpt-5.4", stream: true })
|
||||
expect(responses.model.route.endpoint.baseURL).toBe(
|
||||
"https://gateway.ai.cloudflare.com/v1/test-account/test-gateway/openai",
|
||||
)
|
||||
expect(responses.body).toMatchObject({ model: "gpt-5.4", stream: true })
|
||||
expect(messages.route).toBe("cloudflare-ai-gateway-messages")
|
||||
expect(messages.body).toMatchObject({ model: "anthropic/claude-haiku-4-5" })
|
||||
expect(messages.model.route.endpoint.baseURL).toBe(
|
||||
"https://gateway.ai.cloudflare.com/v1/test-account/test-gateway/anthropic/v1",
|
||||
)
|
||||
expect(messages.body).toMatchObject({ model: "claude-haiku-4-5" })
|
||||
expect(chat.model.route.endpoint.baseURL).toBe("https://api.cloudflare.com/client/v4/accounts/test-account/ai/v1")
|
||||
expect(chat.route).toBe("cloudflare-ai-gateway-chat")
|
||||
expect(chat.body).toMatchObject({ model: "xai/grok-4.6", stream: true })
|
||||
expect(workers.route).toBe("cloudflare-ai-gateway-chat")
|
||||
@@ -100,6 +107,54 @@ describe("Cloudflare", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("posts Claude and OpenAI models to the provider-native gateway endpoints", () =>
|
||||
Effect.gen(function* () {
|
||||
const gateway = CloudflareAIGateway.configure({ accountId: "test-account", apiKey: "test-token", cacheTtl: 300 })
|
||||
const requests: Array<Request> = []
|
||||
const capture = dynamicResponse((input) =>
|
||||
Effect.gen(function* () {
|
||||
requests.push(yield* HttpClientRequest.toWeb(input.request).pipe(Effect.orDie))
|
||||
return input.respond("", { status: 500 })
|
||||
}),
|
||||
)
|
||||
yield* LLM.generate(LLM.request({ model: gateway.model("anthropic/claude-sonnet-4.6"), prompt: "Hi" })).pipe(
|
||||
Effect.provide(capture),
|
||||
Effect.ignore,
|
||||
)
|
||||
yield* LLM.generate(LLM.request({ model: gateway.model("openai/gpt-5-nano"), prompt: "Hi" })).pipe(
|
||||
Effect.provide(capture),
|
||||
Effect.ignore,
|
||||
)
|
||||
|
||||
expect(requests.map((request) => request.url)).toEqual([
|
||||
"https://gateway.ai.cloudflare.com/v1/test-account/default/anthropic/v1/messages",
|
||||
"https://gateway.ai.cloudflare.com/v1/test-account/default/openai/responses",
|
||||
])
|
||||
requests.forEach((request) => {
|
||||
expect(request.headers.get("cf-aig-authorization")).toBe("Bearer test-token")
|
||||
expect(request.headers.get("authorization")).toBeNull()
|
||||
expect(request.headers.get("x-api-key")).toBeNull()
|
||||
expect(request.headers.get("cf-aig-gateway-id")).toBeNull()
|
||||
expect(request.headers.get("cf-aig-cache-ttl")).toBe("300")
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("sends the default gateway ID to the REST API", () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({
|
||||
model: CloudflareAIGateway.configure({ accountId: "test-account", apiKey: "test-token" }).model(
|
||||
"workers-ai/@cf/meta/llama-3.3-70b-instruct",
|
||||
),
|
||||
prompt: "Say hello.",
|
||||
}),
|
||||
)
|
||||
|
||||
expect(prepared.model.route.defaults.headers).toMatchObject({ "cf-aig-gateway-id": "default" })
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("preserves reasoning details for AI Gateway continuation", () =>
|
||||
Effect.gen(function* () {
|
||||
const model = CloudflareAIGateway.configure({
|
||||
@@ -160,12 +215,13 @@ describe("Cloudflare", () => {
|
||||
model: CloudflareAIGateway.configure({
|
||||
baseURL: "https://gateway.proxy.test/v1",
|
||||
apiKey: "test-token",
|
||||
}).model("xai/grok-4.6"),
|
||||
}).model("anthropic/claude-sonnet-4.6"),
|
||||
prompt: "Say hello.",
|
||||
}),
|
||||
)
|
||||
|
||||
expect(prepared.model.route.endpoint.baseURL).toBe("https://gateway.proxy.test/v1")
|
||||
expect(prepared.body).toMatchObject({ model: "anthropic/claude-sonnet-4.6" })
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { describe, expect } from "bun:test"
|
||||
import { Effect } from "effect"
|
||||
import { LLM, LLMRequest } from "../../src/index.js"
|
||||
import { DigitalOcean } from "../../src/providers/digitalocean.js"
|
||||
import { LLMClient } from "../../src/route.js"
|
||||
import {
|
||||
LARGE_CACHEABLE_SYSTEM,
|
||||
expectWeatherToolLoop,
|
||||
goldenWeatherToolLoopRequest,
|
||||
runWeatherToolLoop,
|
||||
} from "../recorded-scenarios.js"
|
||||
import { recordedTests } from "../recorded-test.js"
|
||||
|
||||
const recorded = recordedTests({
|
||||
prefix: "digitalocean-chat",
|
||||
provider: "digitalocean",
|
||||
protocol: "digitalocean-chat",
|
||||
requires: ["DIGITAL_OCEAN_OFFICIAL_API_KEY"],
|
||||
})
|
||||
|
||||
for (const item of [
|
||||
{ id: "anthropic-claude-haiku-4.5", name: "Haiku", maxTokens: 128, providerOptions: undefined },
|
||||
{ id: "openai-gpt-5-nano", name: "GPT Nano", maxTokens: 1024, providerOptions: { reasoningEffort: "minimal" } },
|
||||
] as const) {
|
||||
const model = DigitalOcean.configure({
|
||||
apiKey: process.env.DIGITAL_OCEAN_OFFICIAL_API_KEY ?? "fixture",
|
||||
providerOptions: item.providerOptions,
|
||||
}).model(item.id)
|
||||
|
||||
describe(`DigitalOcean ${item.name} recorded`, () => {
|
||||
recorded.effect.with(
|
||||
`${item.name} reuses a cached prompt`,
|
||||
{ tags: ["cache", "usage"], metadata: { model: item.id } },
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const request = LLM.request({
|
||||
model,
|
||||
system: LARGE_CACHEABLE_SYSTEM,
|
||||
prompt: "Reply exactly: OK",
|
||||
promptCacheKey: `digitalocean-recorded-${item.id}`,
|
||||
generation: { maxTokens: item.maxTokens },
|
||||
})
|
||||
const first = yield* LLMClient.generate(request)
|
||||
const second = yield* LLMClient.generate(request)
|
||||
|
||||
expect(first.text.trim()).toMatch(/^OK\.?$/)
|
||||
expect(second.text.trim()).toMatch(/^OK\.?$/)
|
||||
expect(second.usage.cacheReadInputTokens).toBeGreaterThan(0)
|
||||
for (const response of [first, second]) {
|
||||
expect(response.usage.inputTokens).toBeGreaterThan(4096)
|
||||
expect(response.usage.inputTokens).toBe(
|
||||
(response.usage.nonCachedInputTokens ?? 0) +
|
||||
(response.usage.cacheReadInputTokens ?? 0) +
|
||||
(response.usage.cacheWriteInputTokens ?? 0),
|
||||
)
|
||||
}
|
||||
}),
|
||||
60_000,
|
||||
)
|
||||
|
||||
recorded.effect.with(
|
||||
`${item.name} continues a tool call with cache markers`,
|
||||
{ tags: ["cache", "tool", "tool-loop"], metadata: { model: item.id } },
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const request = goldenWeatherToolLoopRequest({
|
||||
id: `digitalocean-${item.id}-tool-loop`,
|
||||
model,
|
||||
maxTokens: item.maxTokens,
|
||||
temperature: false,
|
||||
})
|
||||
const events = yield* runWeatherToolLoop(LLMRequest.update(request, { cache: "auto" }))
|
||||
expectWeatherToolLoop(events)
|
||||
}),
|
||||
60_000,
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { Effect } from "effect"
|
||||
import { CacheHint, LLM } from "../../src/index.js"
|
||||
import { compileRequest } from "../../src/route/client.js"
|
||||
import { DigitalOcean } from "../../src/providers/digitalocean.js"
|
||||
import { it } from "../lib/effect.js"
|
||||
import { fixedResponse } from "../lib/http.js"
|
||||
import { sseEvents } from "../lib/sse.js"
|
||||
import { LLMClient } from "../../src/route.js"
|
||||
|
||||
describe("DigitalOcean", () => {
|
||||
test("preserves package entrypoint headers and body overrides", () => {
|
||||
const model = DigitalOcean.model("anthropic-claude-fable-5.1", {
|
||||
apiKey: "test-key",
|
||||
headers: { "X-Test": "fixture" },
|
||||
body: { temperature: 0 },
|
||||
})
|
||||
expect(model.route.defaults?.http).toMatchObject({
|
||||
headers: { "X-Test": "fixture" },
|
||||
body: { temperature: 0 },
|
||||
})
|
||||
})
|
||||
|
||||
it.effect("prepares DigitalOcean models with default endpoint and auth", () =>
|
||||
Effect.gen(function* () {
|
||||
const model = DigitalOcean.configure({ apiKey: "test-key" }).model("anthropic-claude-fable-5.1")
|
||||
|
||||
expect(model).toMatchObject({
|
||||
id: "anthropic-claude-fable-5.1",
|
||||
provider: "digitalocean",
|
||||
route: { id: "digitalocean" },
|
||||
})
|
||||
expect(model.route.endpoint.baseURL).toBe("https://inference.do-ai.run/v1")
|
||||
|
||||
const prepared = yield* compileRequest(LLM.request({ model, prompt: "Say hello.", cache: "none" }))
|
||||
|
||||
expect(prepared.route).toBe("digitalocean")
|
||||
expect(prepared.body).toMatchObject({
|
||||
model: "anthropic-claude-fable-5.1",
|
||||
messages: [{ role: "user", content: "Say hello." }],
|
||||
stream: true,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("lowers the native cache policy to DigitalOcean cache_control markers", () =>
|
||||
Effect.gen(function* () {
|
||||
const prepared = yield* compileRequest(
|
||||
LLM.request({
|
||||
model: DigitalOcean.configure({ apiKey: "test-key" }).model("anthropic-claude-fable-5.1"),
|
||||
system: [
|
||||
{ type: "text", text: "Base agent", cache: new CacheHint({ type: "ephemeral", ttlSeconds: 3_600 }) },
|
||||
{ type: "text", text: "Project instructions" },
|
||||
],
|
||||
tools: [{ name: "lookup", description: "Lookup", inputSchema: { type: "object", properties: {} } }],
|
||||
prompt: "Hello",
|
||||
cache: { tools: true, system: true, messages: { tail: 1 } },
|
||||
}),
|
||||
)
|
||||
|
||||
expect(prepared.body).toMatchObject({
|
||||
tools: [{ cache_control: { type: "ephemeral" } }],
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: [
|
||||
{ text: "Base agent", cache_control: { type: "ephemeral", ttl: "1h" } },
|
||||
{ text: "Project instructions", cache_control: { type: "ephemeral" } },
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: [{ text: "Hello", cache_control: { type: "ephemeral" } }],
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("parses DigitalOcean cache usage fields into AI.Usage", () =>
|
||||
Effect.gen(function* () {
|
||||
const model = DigitalOcean.configure({ apiKey: "test-key" }).model("anthropic-claude-fable-5.1")
|
||||
const response = yield* LLMClient.generate(LLM.request({ model, prompt: "Say OK" })).pipe(
|
||||
Effect.provide(
|
||||
fixedResponse(
|
||||
sseEvents(
|
||||
{
|
||||
id: "chatcmpl-1",
|
||||
object: "chat.completion.chunk",
|
||||
created: 1,
|
||||
model: "anthropic-claude-fable-5.1",
|
||||
choices: [{ index: 0, delta: { content: "OK" }, finish_reason: null }],
|
||||
},
|
||||
{
|
||||
id: "chatcmpl-1",
|
||||
object: "chat.completion.chunk",
|
||||
created: 1,
|
||||
model: "anthropic-claude-fable-5.1",
|
||||
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
||||
usage: {
|
||||
prompt_tokens: 4491,
|
||||
completion_tokens: 8,
|
||||
total_tokens: 4499,
|
||||
cache_read_input_tokens: 4483,
|
||||
cache_created_input_tokens: 0,
|
||||
},
|
||||
},
|
||||
"[DONE]",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
expect(response.usage).toMatchObject({
|
||||
inputTokens: 4491,
|
||||
outputTokens: 8,
|
||||
nonCachedInputTokens: 8,
|
||||
cacheReadInputTokens: 4483,
|
||||
totalTokens: 4499,
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
@@ -1285,25 +1285,23 @@ describe("Gemini route", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
expect(prepared.body.contents).toEqual([
|
||||
{
|
||||
role: "model",
|
||||
parts: [
|
||||
{
|
||||
functionCall: { id: "tool_0", name: "lookup", args: { query: "weather" } },
|
||||
thoughtSignature: "parallel_signature",
|
||||
},
|
||||
{
|
||||
functionCall: { id: "tool_1", name: "lookup", args: { query: "news" } },
|
||||
thoughtSignature: undefined,
|
||||
},
|
||||
{
|
||||
functionCall: { id: "tool_2", name: "lookup", args: { query: "sports" } },
|
||||
thoughtSignature: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
expect(prepared.body.contents[0]).toEqual({
|
||||
role: "model",
|
||||
parts: [
|
||||
{
|
||||
functionCall: { id: "tool_0", name: "lookup", args: { query: "weather" } },
|
||||
thoughtSignature: "parallel_signature",
|
||||
},
|
||||
{
|
||||
functionCall: { id: "tool_1", name: "lookup", args: { query: "news" } },
|
||||
thoughtSignature: undefined,
|
||||
},
|
||||
{
|
||||
functionCall: { id: "tool_2", name: "lookup", args: { query: "sports" } },
|
||||
thoughtSignature: undefined,
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -1321,21 +1319,19 @@ describe("Gemini route", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
expect(prepared.body.contents).toEqual([
|
||||
{
|
||||
role: "model",
|
||||
parts: [
|
||||
{
|
||||
functionCall: { id: "tool_0", name: "lookup", args: { query: "weather" } },
|
||||
thoughtSignature: "skip_thought_signature_validator",
|
||||
},
|
||||
{
|
||||
functionCall: { id: "tool_1", name: "lookup", args: { query: "news" } },
|
||||
thoughtSignature: "skip_thought_signature_validator",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
expect(prepared.body.contents[0]).toEqual({
|
||||
role: "model",
|
||||
parts: [
|
||||
{
|
||||
functionCall: { id: "tool_0", name: "lookup", args: { query: "weather" } },
|
||||
thoughtSignature: "skip_thought_signature_validator",
|
||||
},
|
||||
{
|
||||
functionCall: { id: "tool_1", name: "lookup", args: { query: "news" } },
|
||||
thoughtSignature: "skip_thought_signature_validator",
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ const cloudflareWorkers = CloudflareWorkersAI.configure({
|
||||
})
|
||||
const cloudflareAIGatewayWorkers = cloudflareAIGateway.model("workers-ai/@cf/meta/llama-3.1-8b-instruct")
|
||||
const cloudflareAIGatewayWorkersTools = cloudflareAIGateway.model("workers-ai/@cf/openai/gpt-oss-20b")
|
||||
const cloudflareAIGatewayClaude = cloudflareAIGateway.model("anthropic/claude-haiku-4.5")
|
||||
const cloudflareAIGatewayOpenAI = cloudflareAIGateway.model("openai/gpt-5-nano")
|
||||
const cloudflareWorkersAI = cloudflareWorkers.model("@cf/meta/llama-3.1-8b-instruct")
|
||||
const cloudflareWorkersAITools = cloudflareWorkers.model("@cf/openai/gpt-oss-20b")
|
||||
const deepseek = DeepSeek.configure({ apiKey: process.env.DEEPSEEK_API_KEY ?? "fixture" }).model("deepseek-chat")
|
||||
@@ -76,7 +78,7 @@ const openrouterOpus = OpenRouter.configure({
|
||||
const redactCloudflareURL = (url: string) =>
|
||||
url
|
||||
.replace(/\/client\/v4\/accounts\/[^/]+\/ai\/v1\//, "/client/v4/accounts/{account}/ai/v1/")
|
||||
.replace(/\/v1\/[^/]+\/[^/]+\/compat\//, "/v1/{account}/{gateway}/compat/")
|
||||
.replace(/\/v1\/[^/]+\/[^/]+\/(anthropic|openai)\//, "/v1/{account}/{gateway}/$1/")
|
||||
|
||||
const cloudflareOptions = {
|
||||
redact: { url: redactCloudflareURL },
|
||||
@@ -177,6 +179,25 @@ describeRecordedGoldenScenarios([
|
||||
options: cloudflareOptions,
|
||||
scenarios: [{ id: "tool-call", maxTokens: 120 }],
|
||||
},
|
||||
{
|
||||
name: "Cloudflare AI Gateway Claude Haiku 4.5",
|
||||
prefix: "cloudflare-ai-gateway",
|
||||
model: cloudflareAIGatewayClaude,
|
||||
requires: ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN"],
|
||||
options: { redact: { url: redactCloudflareURL, allowRequestHeaders: ["anthropic-version"] } },
|
||||
scenarios: ["text", "tool-loop"],
|
||||
},
|
||||
{
|
||||
name: "Cloudflare AI Gateway OpenAI GPT-5 Nano",
|
||||
prefix: "cloudflare-ai-gateway",
|
||||
model: cloudflareAIGatewayOpenAI,
|
||||
requires: ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN"],
|
||||
options: cloudflareOptions,
|
||||
scenarios: [
|
||||
{ id: "text", temperature: false, maxTokens: 2000 },
|
||||
{ id: "tool-loop", temperature: false, maxTokens: 2000, timeout: 30_000 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Cloudflare Workers AI Llama 3.1 8B",
|
||||
prefix: "cloudflare-workers-ai",
|
||||
|
||||
@@ -302,6 +302,7 @@ describe("Google Vertex providers", () => {
|
||||
|
||||
expect(model.provider).toBe("google-vertex")
|
||||
expect(response.text).toBe("Hello.")
|
||||
expect(response.usage?.providerMetadata).toHaveProperty("vertex")
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -171,6 +171,7 @@ describe("Groq recorded", () => {
|
||||
expect(response.text).not.toContain("<think>")
|
||||
expect(response.reasoning.length).toBeGreaterThan(0)
|
||||
expect(response.events.some(LLMEvent.is.reasoningDelta)).toBe(true)
|
||||
expect(response.usage?.providerMetadata).toHaveProperty("groq")
|
||||
expectUsage(response)
|
||||
}),
|
||||
60_000,
|
||||
|
||||
@@ -215,13 +215,11 @@ describe("Mistral Chat", () => {
|
||||
],
|
||||
}),
|
||||
)
|
||||
expect(prepared.body.messages).toEqual([
|
||||
{
|
||||
role: "assistant",
|
||||
content: "",
|
||||
tool_calls: [{ id: "Ab12Cd34E", type: "function", function: { name: "lookup", arguments: "{}" } }],
|
||||
},
|
||||
])
|
||||
expect(prepared.body.messages[0]).toEqual({
|
||||
role: "assistant",
|
||||
content: "",
|
||||
tool_calls: [{ id: "Ab12Cd34E", type: "function", function: { name: "lookup", arguments: "{}" } }],
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -315,7 +313,9 @@ describe("Mistral Chat", () => {
|
||||
expect(response.reasoning).toBe("Consider")
|
||||
expect(response.text).toBe("Answer")
|
||||
expect(response.events.find(LLMEvent.is.reasoningStart)?.providerMetadata).toBeUndefined()
|
||||
expect(response.events.filter(LLMEvent.is.reasoningDelta).map((event) => [event.text, event.providerMetadata])).toEqual([
|
||||
expect(
|
||||
response.events.filter(LLMEvent.is.reasoningDelta).map((event) => [event.text, event.providerMetadata]),
|
||||
).toEqual([
|
||||
["Con", undefined],
|
||||
["sider", undefined],
|
||||
])
|
||||
|
||||
@@ -48,7 +48,7 @@ describe("native OpenAI-compatible providers", () => {
|
||||
[GoogleVertex.configure(vertex).model("model"), "vertex"],
|
||||
[GoogleVertexChat.configure(vertex).model("model"), "vertex"],
|
||||
[GoogleVertexResponses.configure(vertex).model("model"), "vertex"],
|
||||
[GoogleVertexMessages.configure(vertex).model("model"), "anthropic"],
|
||||
[GoogleVertexMessages.configure(vertex).model("model"), "vertex"],
|
||||
[Anthropic.configure({ apiKey: "test" }).model("model"), "anthropic"],
|
||||
[
|
||||
AnthropicCompatible.configure({ baseURL: "https://example.test/v1", provider: "minimax" }).model("model"),
|
||||
@@ -64,6 +64,7 @@ describe("native OpenAI-compatible providers", () => {
|
||||
[DeepSeek.configure({ apiKey: "test" }).model("model"), "deepseek"],
|
||||
[Fireworks.configure({ apiKey: "test" }).model("model"), "fireworks"],
|
||||
[DeepInfra.configure({ apiKey: "test" }).model("model"), "deepinfra"],
|
||||
[Groq.configure({ apiKey: "test" }).model("model"), "groq"],
|
||||
[TogetherAI.configure({ apiKey: "test" }).model("model"), "togetherai"],
|
||||
[CloudflareAIGateway.configure({ accountId: "account" }).model("model"), "cloudflare-ai-gateway"],
|
||||
[CloudflareWorkersAI.configure({ accountId: "account" }).model("model"), "cloudflare-workers-ai"],
|
||||
|
||||
@@ -1257,21 +1257,19 @@ describe("OpenAI Chat route", () => {
|
||||
})
|
||||
|
||||
const replay = yield* compileRequest(LLM.request({ model, messages: [response.message] }))
|
||||
expect(replay.body.messages).toEqual([
|
||||
{
|
||||
role: "assistant",
|
||||
content: null,
|
||||
reasoning: "thinking",
|
||||
reasoning_details: details,
|
||||
tool_calls: [
|
||||
{
|
||||
id: "call_1",
|
||||
type: "function",
|
||||
function: { name: "lookup", arguments: '{"query":"weather"}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
expect(replay.body.messages[0]).toEqual({
|
||||
role: "assistant",
|
||||
content: null,
|
||||
reasoning: "thinking",
|
||||
reasoning_details: details,
|
||||
tool_calls: [
|
||||
{
|
||||
id: "call_1",
|
||||
type: "function",
|
||||
function: { name: "lookup", arguments: '{"query":"weather"}' },
|
||||
},
|
||||
],
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -1374,18 +1372,16 @@ describe("OpenAI Chat route", () => {
|
||||
})
|
||||
|
||||
const replay = yield* compileRequest(LLM.request({ model, messages: [response.message] }))
|
||||
expect(replay.body.messages).toEqual([
|
||||
{
|
||||
role: "assistant",
|
||||
content: null,
|
||||
tool_calls: [{ id: "call_1", type: "function", function: { name: "get_time", arguments: "{}" } }],
|
||||
reasoning_content: "Let me think",
|
||||
reasoning_details: [
|
||||
{ type: "summary", summary: "Plan tools" },
|
||||
{ type: "encrypted", encrypted: "opaque" },
|
||||
],
|
||||
},
|
||||
])
|
||||
expect(replay.body.messages[0]).toEqual({
|
||||
role: "assistant",
|
||||
content: null,
|
||||
tool_calls: [{ id: "call_1", type: "function", function: { name: "get_time", arguments: "{}" } }],
|
||||
reasoning_content: "Let me think",
|
||||
reasoning_details: [
|
||||
{ type: "summary", summary: "Plan tools" },
|
||||
{ type: "encrypted", encrypted: "opaque" },
|
||||
],
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -2209,7 +2205,7 @@ describe("OpenAI Chat route", () => {
|
||||
)
|
||||
|
||||
expect((yield* Ref.get(events)).some((event) => event.type === "text-delta")).toBeTrue()
|
||||
expect(error.message).toBe("ECONNRESET: socket closed unexpectedly")
|
||||
expect(error.message).toBe("Connection lost while reading the response: ECONNRESET: socket closed unexpectedly")
|
||||
expect(error.reason).toMatchObject({
|
||||
_tag: "Transport",
|
||||
transport: "http",
|
||||
@@ -2227,7 +2223,7 @@ describe("OpenAI Chat route", () => {
|
||||
Effect.flip,
|
||||
)
|
||||
|
||||
expect(error.message).toBe("ECONNRESET: socket closed before output")
|
||||
expect(error.message).toBe("Connection lost while reading the response: ECONNRESET: socket closed before output")
|
||||
expect(error.reason).toMatchObject({
|
||||
_tag: "Transport",
|
||||
transport: "http",
|
||||
|
||||
@@ -3804,6 +3804,8 @@ describe("OpenAI Responses route", () => {
|
||||
name: "lookup",
|
||||
arguments: '{"query":"news"}',
|
||||
},
|
||||
{ type: "function_call_output", call_id: "call_1", output: "Tool result missing" },
|
||||
{ type: "function_call_output", call_id: "call_2", output: "Tool result missing" },
|
||||
])
|
||||
}),
|
||||
)
|
||||
@@ -4098,15 +4100,13 @@ describe("OpenAI Responses route", () => {
|
||||
])
|
||||
|
||||
const prepared = yield* compileRequest(LLM.request({ model, messages: [response.message] }))
|
||||
expect(prepared.body.input).toEqual([
|
||||
{
|
||||
type: "function_call",
|
||||
id: "fc_item_1",
|
||||
call_id: "call_1",
|
||||
name: "lookup",
|
||||
arguments: '{"query":"weather"}',
|
||||
},
|
||||
])
|
||||
expect(prepared.body.input[0]).toEqual({
|
||||
type: "function_call",
|
||||
id: "fc_item_1",
|
||||
call_id: "call_1",
|
||||
name: "lookup",
|
||||
arguments: '{"query":"weather"}',
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -4998,11 +4998,11 @@ describe("OpenAI Responses route", () => {
|
||||
|
||||
it.effect("retains the token-sharing HTTP 429 body for retry hooks", () =>
|
||||
Effect.gen(function* () {
|
||||
const body = JSON.stringify({ error: { code: "subscription_sharing_usage_limit_exceeded", message: "Rate limit exceeded" } })
|
||||
const body = JSON.stringify({
|
||||
error: { code: "subscription_sharing_usage_limit_exceeded", message: "Rate limit exceeded" },
|
||||
})
|
||||
const error = yield* LLMClient.generate(request).pipe(
|
||||
Effect.provide(
|
||||
fixedResponse(body, { status: 429, headers: { "content-type": "application/json" } }),
|
||||
),
|
||||
Effect.provide(fixedResponse(body, { status: 429, headers: { "content-type": "application/json" } })),
|
||||
Effect.flip,
|
||||
)
|
||||
|
||||
|
||||
@@ -54,16 +54,28 @@ it.effect("ZAI Chat retains native thinking fields and scopes tool streaming to
|
||||
baseURL: "https://gateway.example/custom",
|
||||
auth: Auth.header("x-test-key", "fixture"),
|
||||
})
|
||||
for (const modelID of [
|
||||
"glm-4.5",
|
||||
"glm-4.5-air",
|
||||
"glm-4.5-x",
|
||||
"glm-4.6",
|
||||
"glm-4.7-flash",
|
||||
"glm-5.3",
|
||||
"glm-5.3-flash",
|
||||
"future-model",
|
||||
]) {
|
||||
for (const [modelID, supported] of [
|
||||
["glm-3.9", false],
|
||||
["glm-4", false],
|
||||
["glm-4.5", false],
|
||||
["glm-4.5-air", false],
|
||||
["glm-4.5-x", false],
|
||||
["glm-4.6", true],
|
||||
["glm-4.7-flash", true],
|
||||
["glm-4.8", true],
|
||||
["glm-4.10", true],
|
||||
["glm-5", true],
|
||||
["glm-5.3", true],
|
||||
["glm-5.3-flash", true],
|
||||
["glm-6", true],
|
||||
["glm-10.1-new-family", true],
|
||||
["zai/glm-6.1", true],
|
||||
["GLM-4.6", true],
|
||||
["not-glm-6", false],
|
||||
["glm-4.60foo", false],
|
||||
["glm-6.x", false],
|
||||
["future-model", false],
|
||||
] as const) {
|
||||
const result = yield* compileRequest(
|
||||
LLM.request({
|
||||
model: provider.chat(modelID),
|
||||
@@ -78,9 +90,7 @@ it.effect("ZAI Chat retains native thinking fields and scopes tool streaming to
|
||||
max_tokens: 123,
|
||||
thinking: { type: "enabled", clear_thinking: false },
|
||||
})
|
||||
expect(result.body.tool_stream).toBe(
|
||||
["glm-4.6", "glm-4.7-flash", "glm-5.3", "glm-5.3-flash"].includes(modelID) ? true : undefined,
|
||||
)
|
||||
expect(result.body.tool_stream).toBe(supported ? true : undefined)
|
||||
expect(result.body.max_completion_tokens).toBeUndefined()
|
||||
expect(result.body.store).toBeUndefined()
|
||||
expect(result.body.tools).toEqual([
|
||||
@@ -120,6 +130,30 @@ it.effect("ZAI Chat retains native thinking fields and scopes tool streaming to
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("ZAI tool streaming defaults require tools and preserve explicit overrides", () =>
|
||||
Effect.gen(function* () {
|
||||
for (const configure of [ZAI.configure, ZAICodingPlan.configure]) {
|
||||
const provider = configure({ apiKey: "fixture" })
|
||||
for (const item of [
|
||||
{ id: "glm-6.1", tools: [], toolStream: undefined, expected: undefined },
|
||||
{ id: "glm-4.5", tools: [tool], toolStream: true, expected: true },
|
||||
{ id: "glm-6.1", tools: [tool], toolStream: false, expected: false },
|
||||
{ id: "future-model", tools: [tool], toolStream: true, expected: true },
|
||||
]) {
|
||||
const result = yield* compileRequest(
|
||||
LLM.request({
|
||||
model: provider.chat(item.id),
|
||||
prompt: "Hello",
|
||||
tools: item.tools,
|
||||
providerOptions: { toolStream: item.toolStream },
|
||||
}),
|
||||
)
|
||||
expect(result.body.tool_stream).toBe(item.expected)
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("ZAI lowers effort using the selected native API without inventing thinking settings", () =>
|
||||
Effect.gen(function* () {
|
||||
for (const effort of ["none", "minimal", "low", "medium", "high", "xhigh", "max", "future-effort"]) {
|
||||
|
||||
@@ -15,12 +15,47 @@ describe("tool history normalization", () => {
|
||||
Message.assistant(toolCall("trailing")),
|
||||
])
|
||||
|
||||
expect(normalized.map((message) => message.role)).toEqual(["assistant", "tool", "tool", "user", "assistant"])
|
||||
expect(normalized.map((message) => message.role)).toEqual([
|
||||
"assistant",
|
||||
"tool",
|
||||
"tool",
|
||||
"user",
|
||||
"assistant",
|
||||
"tool",
|
||||
])
|
||||
expect(normalized[1]?.content[0]).toMatchObject({ type: "tool-result", id: "first", name: "first" })
|
||||
expect(normalized[2]?.content).toEqual([
|
||||
{ type: "tool-result", id: "second", name: "second", result: { type: "error", value: "Tool result missing" } },
|
||||
])
|
||||
expect(normalized[4]?.content).toEqual([toolCall("trailing")])
|
||||
expect(normalized[5]?.content).toEqual([
|
||||
{
|
||||
type: "tool-result",
|
||||
id: "trailing",
|
||||
name: "trailing",
|
||||
result: { type: "error", value: "Tool result missing" },
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test("fills missing results for trailing calls without replacing available results", () => {
|
||||
const calls = Message.assistant([toolCall("answered"), toolCall("unanswered")])
|
||||
const answered = toolResult("answered", "done", "answered", "text")
|
||||
const hosted = ToolCallPart.make({ id: "hosted", name: "web_search", input: {}, providerExecuted: true })
|
||||
|
||||
expect(normalizeToolHistory([calls, answered])).toEqual([
|
||||
calls,
|
||||
answered,
|
||||
Message.tool(
|
||||
ToolResultPart.make({
|
||||
id: "unanswered",
|
||||
name: "unanswered",
|
||||
result: "Tool result missing",
|
||||
resultType: "error",
|
||||
}),
|
||||
),
|
||||
])
|
||||
expect(normalizeToolHistory([Message.assistant(hosted)])).toEqual([Message.assistant(hosted)])
|
||||
})
|
||||
|
||||
test("normalizes empty results without changing whitespace or media", () => {
|
||||
@@ -77,4 +112,51 @@ describe("tool history normalization", () => {
|
||||
|
||||
expect(normalized[1]?.content[0]).toMatchObject({ name: "lookup", namespace: undefined })
|
||||
})
|
||||
|
||||
test("moves system updates after the results of pending calls", () => {
|
||||
const calls = Message.assistant([toolCall("first"), toolCall("second")])
|
||||
const first = toolResult("first", "one", "first", "text")
|
||||
const second = toolResult("second", "two", "second", "text")
|
||||
const update = Message.system("First update.")
|
||||
const later = Message.system("Second update.")
|
||||
const user = Message.user("Continue.")
|
||||
const missing = (id: string) =>
|
||||
Message.tool(ToolResultPart.make({ id, name: id, result: "Tool result missing", resultType: "error" }))
|
||||
|
||||
expect(normalizeToolHistory([calls, first, update, later, second, user])).toEqual([
|
||||
calls,
|
||||
first,
|
||||
second,
|
||||
update,
|
||||
later,
|
||||
user,
|
||||
])
|
||||
expect(normalizeToolHistory([Message.assistant(toolCall("first")), update, user])).toEqual([
|
||||
Message.assistant(toolCall("first")),
|
||||
missing("first"),
|
||||
update,
|
||||
user,
|
||||
])
|
||||
expect(normalizeToolHistory([Message.assistant(toolCall("first")), update])).toEqual([
|
||||
Message.assistant(toolCall("first")),
|
||||
missing("first"),
|
||||
update,
|
||||
])
|
||||
})
|
||||
|
||||
test("moves effort updates after the results of pending calls", () => {
|
||||
const call = Message.assistant(toolCall("first"))
|
||||
const result = toolResult("first", "one", "first", "text")
|
||||
const effort = Message.effort({ effort: "low", previous: "high" })
|
||||
|
||||
expect(normalizeToolHistory([call, effort, result])).toEqual([call, result, effort])
|
||||
})
|
||||
|
||||
test("keeps system updates in place when no call is pending", () => {
|
||||
const history = [Message.assistant(toolCall("first")), toolResult("first", "one", "first", "text")]
|
||||
const update = Message.system("Update.")
|
||||
const input = [Message.user("Start."), update, ...history, update, Message.user("Continue.")]
|
||||
|
||||
expect(normalizeToolHistory(input)).toBe(input)
|
||||
})
|
||||
})
|
||||
@@ -43,6 +43,18 @@
|
||||
- In translation review notes, name the corpora used and call out uncertain or region-specific terminology so native speakers can focus review where it matters.
|
||||
- Also use the relevant language authority or official dictionary for the locale (for example RAE/Fundéu, FranceTerme, Duden, TDK, Kotus/Kielitoimiston sanakirja, Språkrådet/Bokmålsordboka, Rada Języka Polskiego/PWN, the Russian and Arabic language academies, the Ukrainian Orthography, Taiwan MOE dictionaries, or the Royal Society of Thailand). Treat the English dictionary as the semantic source of truth and preserve placeholders, code identifiers, product names, and keyboard labels.
|
||||
|
||||
## Tests
|
||||
|
||||
A test must pay for its upkeep. Before adding one, answer three questions: which observable contract does it protect, which credible regression makes it fail, and why does no existing test already catch that regression. If any answer is missing, do not add the test.
|
||||
|
||||
- One contract, one owner. Test each behavior once, at the strongest boundary that observes it: the area's e2e keeper suite, or the owning module's unit test. Extend an existing table or keeper case before creating a file.
|
||||
- Do not unit-test what an e2e keeper already proves, what a dependency does (for example `@pierre/trees`), config that passes values through, or source text (import or string greps). Source text is fair game only when that text is the contract, such as a persisted key.
|
||||
- Never add production seams for tests: no test-only exports, parameters such as `now`, `delay` or `budget`, flags, globals, or `data-*` attributes. Control time with `setSystemTime` from `bun:test` and drive the real entry point.
|
||||
- A test must be able to fail. Do not assert values the code under test produced, mock the behavior you assert, use soft assertions for the order the test guards, or leave `rejects`/`resolves` un-awaited. A regression test must fail on the code before the fix.
|
||||
- Do not repeat a case per theme, text direction, viewport or channel. Use one table, and only when the behavior differs.
|
||||
- When you delete or move coverage, name the test that still proves the contract. When you move a contract, mutate its owner once and confirm the new test fails.
|
||||
- A contract proven by a `packages/session-ui/component-tests` test does not also get an app e2e copy.
|
||||
|
||||
## Tool Calling
|
||||
|
||||
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { fileURLToPath } from "node:url"
|
||||
import { expect, story } from "../../storybook/playwright/story"
|
||||
|
||||
const source = (path: string) => `/@fs/${fileURLToPath(new URL(path, import.meta.url)).replaceAll("\\", "/")}`
|
||||
const modules = {
|
||||
fixture: source("../../gui-extensions/src/browser/panel.fixture.tsx"),
|
||||
host: source("../src/runtime/extension/host.tsx"),
|
||||
panels: source("../src/runtime/extension/panels.tsx"),
|
||||
language: source("../src/runtime/i18n/language.tsx"),
|
||||
browser: source("../../gui-extensions/src/browser/index.ts"),
|
||||
browserRenderer: source("../../gui-extensions/src/browser/renderer.tsx"),
|
||||
file: source("../../gui-extensions/src/file/index.ts"),
|
||||
fileRenderer: source("../../gui-extensions/src/file/renderer.tsx"),
|
||||
}
|
||||
|
||||
story(
|
||||
"keeps a restored browser tab selected and undrawn until the desktop's first inventory",
|
||||
async ({ mount, page }) => {
|
||||
// Any story loads the app styles; the fixture mounts the real side region and extensions beside it.
|
||||
await mount("ui-line-comment--editor")
|
||||
await page.evaluate(async (modules) => {
|
||||
const [{ mountBrowserRegion }, host, panels, language, browser, file] = await Promise.all([
|
||||
import(modules.fixture),
|
||||
import(modules.host),
|
||||
import(modules.panels),
|
||||
import(modules.language),
|
||||
import(modules.browser),
|
||||
import(modules.file),
|
||||
])
|
||||
mountBrowserRegion({
|
||||
LanguageProvider: language.LanguageProvider,
|
||||
ExtensionHostProvider: host.ExtensionHostProvider,
|
||||
useExtensionHost: host.useExtensionHost,
|
||||
createRegion: panels.createRegion,
|
||||
definitions: [
|
||||
{ ...browser.default, renderer: () => import(modules.browserRenderer) },
|
||||
{ ...file.default, renderer: () => import(modules.fileRenderer) },
|
||||
],
|
||||
})
|
||||
}, modules)
|
||||
const root = page.getByTestId("browser-region-fixture")
|
||||
const tabs = root.getByRole("tab")
|
||||
const tree = root.getByTestId("tree")
|
||||
await expect(root.getByText("Registrations: 1", { exact: true })).toBeVisible()
|
||||
await expect(tabs).toHaveText(["alpha.ts"])
|
||||
await expect(tree).toHaveText('{"tab":"changes"}')
|
||||
|
||||
// Beta was left on its browser tab, which the desktop has not reported yet.
|
||||
await root.getByRole("button", { name: "Beta", exact: true }).click()
|
||||
await expect(root.getByText("Registrations: 2", { exact: true })).toBeVisible()
|
||||
await expect(root.getByTestId("selected")).toHaveText(/^browser:tab_/)
|
||||
await expect(tabs).toHaveText(["beta.ts"])
|
||||
// No fallback tab was selected, so the file tab's selection never switched the tree to All files.
|
||||
await expect(tree).toHaveText('{"tab":"changes"}')
|
||||
|
||||
await root.getByRole("button", { name: "First inventory", exact: true }).click()
|
||||
await expect(tabs).toHaveText(["beta.ts", "Preview"])
|
||||
await expect(root.getByRole("tab", { name: "Preview", exact: true })).toHaveAttribute("aria-selected", "true")
|
||||
await expect(tree).toHaveText('{"tab":"changes"}')
|
||||
},
|
||||
)
|
||||
@@ -1,242 +0,0 @@
|
||||
import { DialogProvider } from "@opencode/ui/context/dialog"
|
||||
import { Browser } from "@opencode/plugin-browser/rpc"
|
||||
import { For, Show } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { Portal, render } from "solid-js/web"
|
||||
import { LanguageProvider, UiI18nBridge } from "../src/runtime/i18n/language"
|
||||
import type { BrowserPaneEvent, BrowserPaneLayout, BrowserPaneRegistration } from "../src/runtime/platform/browser-pane"
|
||||
import type { createSessionBrowser } from "../src/session/browser/model"
|
||||
import { SessionBrowserPane, type SessionBrowserComment } from "../src/session/browser/pane"
|
||||
|
||||
export function mountBrowserPane() {
|
||||
const host = document.createElement("main")
|
||||
host.dataset.testid = "browser-pane-fixture"
|
||||
host.style.cssText = "position:fixed;inset:0;z-index:1000;background:#181818;color:#eee;padding:24px"
|
||||
document.body.appendChild(host)
|
||||
|
||||
function Fixture() {
|
||||
const [store, setStore] = createStore({
|
||||
session: "Alpha",
|
||||
mounted: true,
|
||||
visible: true,
|
||||
url: undefined as string | undefined,
|
||||
loading: false,
|
||||
generation: 0,
|
||||
delayNavigation: false,
|
||||
pendingURL: undefined as string | undefined,
|
||||
loadErrors: {} as Record<string, string | undefined>,
|
||||
error: undefined as string | undefined,
|
||||
layouts: {} as Record<string, BrowserPaneLayout | undefined>,
|
||||
covered: false,
|
||||
captures: 0,
|
||||
holdCapture: false,
|
||||
picker: {} as Record<string, boolean | undefined>,
|
||||
highlights: [] as string[],
|
||||
comments: [] as SessionBrowserComment[],
|
||||
})
|
||||
// Each capture waits until the fixture releases it, so a spec can observe the pending state.
|
||||
const held: (() => void)[] = []
|
||||
const inspectors = new Set<(event: Extract<BrowserPaneEvent, { type: "inspect" }>) => void>()
|
||||
const emitInspect = (event: Extract<BrowserPaneEvent, { type: "inspect" }>) =>
|
||||
inspectors.forEach((listener) => listener(event))
|
||||
const tabs = ["Alpha", "Beta"].map((name) => ({
|
||||
id: Browser.TabID.make(`tab_${name === "Alpha" ? "11111111" : "22222222"}-1111-1111-1111-111111111111`),
|
||||
title: name,
|
||||
url: `https://${name.toLowerCase()}.example/`,
|
||||
loading: false,
|
||||
canGoBack: false,
|
||||
canGoForward: false,
|
||||
generation: 0,
|
||||
}))
|
||||
// Record the native boundary per registration: hiding Beta cannot hide Alpha's view.
|
||||
const registrations = new Map<string, BrowserPaneRegistration>(
|
||||
tabs.map((tab) => [
|
||||
tab.title,
|
||||
{
|
||||
setLayout: (layout) => setStore("layouts", tab.title, layout),
|
||||
command: async () => undefined,
|
||||
capture: async () => {
|
||||
setStore("captures", (count) => count + 1)
|
||||
if (store.holdCapture) await new Promise<void>((resolve) => held.push(resolve))
|
||||
const canvas = new OffscreenCanvas(4, 4)
|
||||
const paint = canvas.getContext("2d")
|
||||
if (paint) {
|
||||
paint.fillStyle = "#3b82f6"
|
||||
paint.fillRect(0, 0, 4, 4)
|
||||
}
|
||||
return canvas.convertToBlob()
|
||||
},
|
||||
// The desktop confirms each picker change, as the page does once inspect mode is armed.
|
||||
inspect: (tabID, enabled) => {
|
||||
setStore("picker", tab.title, enabled)
|
||||
emitInspect({ type: "inspect", tabID, active: enabled })
|
||||
},
|
||||
highlight: (_tabID, ref) => setStore("highlights", (items) => [...items, ref ?? "clear"]),
|
||||
close: () => undefined,
|
||||
},
|
||||
]),
|
||||
)
|
||||
const browser: ReturnType<typeof createSessionBrowser> = {
|
||||
available: () => true,
|
||||
attached: () => !!registrations.get(store.session),
|
||||
opened: () => !!registrations.get(store.session),
|
||||
state: () => ({ tabs: tabs.filter((tab) => tab.title === store.session), focusedTabID: null }),
|
||||
tabs: () => tabs.filter((tab) => tab.title === store.session),
|
||||
active: () => {
|
||||
const tab = tabs.find((tab) => tab.title === store.session) ?? tabs[0]
|
||||
return {
|
||||
...tab,
|
||||
url: store.url ?? tab.url,
|
||||
loading: store.loading,
|
||||
generation: store.generation,
|
||||
loadError: store.loadErrors[store.session],
|
||||
}
|
||||
},
|
||||
registration: () => registrations.get(store.session),
|
||||
error: () => store.error ?? (store.loadErrors[store.session] ? "Request failed" : undefined),
|
||||
suspended: () => false,
|
||||
close: () => undefined,
|
||||
open: () => undefined,
|
||||
command: (command) => {
|
||||
setStore("error", undefined)
|
||||
if (command.type === "navigate" || command.type === "reload") setStore("loadErrors", store.session, undefined)
|
||||
if (command.type === "navigate") {
|
||||
if (store.delayNavigation) {
|
||||
setStore("pendingURL", command.url)
|
||||
return
|
||||
}
|
||||
setStore({ url: command.url, loading: false, generation: store.generation + 1 })
|
||||
}
|
||||
if (command.type === "stop") setStore("loading", false)
|
||||
},
|
||||
onInspect: (listener) => {
|
||||
inspectors.add(listener)
|
||||
return () => inspectors.delete(listener)
|
||||
},
|
||||
reveal: () => undefined,
|
||||
}
|
||||
const pick = () =>
|
||||
emitInspect({
|
||||
type: "inspect",
|
||||
tabID: browser.active().id,
|
||||
active: false,
|
||||
element: {
|
||||
ref: Browser.Ref.make("e7"),
|
||||
selector: "main > button.primary",
|
||||
label: "button.primary",
|
||||
role: "button",
|
||||
name: "Save changes",
|
||||
rect: { x: 48, y: 40, width: 160, height: 36 },
|
||||
},
|
||||
})
|
||||
return (
|
||||
<>
|
||||
<h1 style={{ "font-size": "24px", "margin-bottom": "16px" }}>Browser pane lifecycle</h1>
|
||||
<p>Selected session: {store.session}</p>
|
||||
<nav style={{ display: "flex", gap: "20px", margin: "16px 0" }}>
|
||||
<For each={["Alpha", "Beta", "Empty"]}>
|
||||
{(name) => <button onClick={() => setStore({ session: name, mounted: name !== "Empty" })}>{name}</button>}
|
||||
</For>
|
||||
<button onClick={() => setStore("mounted", false)}>Unmount pane</button>
|
||||
<button onClick={() => setStore({ url: "about:blank", loading: false })}>Blank page</button>
|
||||
<button onClick={() => setStore({ url: "about:blank", loading: true })}>Loading page</button>
|
||||
<button
|
||||
onClick={() =>
|
||||
setStore({ loading: true, generation: store.generation + 1, loadErrors: { [store.session]: undefined } })
|
||||
}
|
||||
>
|
||||
Load current page
|
||||
</button>
|
||||
<button onClick={() => setStore("loadErrors", store.session, "ERR_CONNECTION_REFUSED")}>Failed page</button>
|
||||
<button onClick={() => setStore("delayNavigation", true)}>Delay navigation</button>
|
||||
<button onClick={() => setStore({ error: "ERR_BLOCKED_BY_CLIENT", pendingURL: undefined })}>
|
||||
Block navigation
|
||||
</button>
|
||||
<button
|
||||
onClick={() =>
|
||||
setStore({
|
||||
url: store.pendingURL,
|
||||
pendingURL: undefined,
|
||||
loading: false,
|
||||
generation: store.generation + 1,
|
||||
})
|
||||
}
|
||||
>
|
||||
Complete navigation
|
||||
</button>
|
||||
<button onClick={() => setStore("visible", (visible) => !visible)}>Toggle Review tab</button>
|
||||
<button onClick={() => setStore("holdCapture", true)}>Hold capture</button>
|
||||
<button onClick={() => held.splice(0).forEach((resolve) => resolve())}>Release capture</button>
|
||||
<button onClick={() => setStore("covered", (covered) => !covered)}>Toggle popover</button>
|
||||
<button onClick={pick}>Pick element</button>
|
||||
</nav>
|
||||
<p>Captures: {store.captures}</p>
|
||||
<p>Picker: {store.picker[store.session] ? "on" : "off"}</p>
|
||||
<p>Highlights: {store.highlights.join(",")}</p>
|
||||
<div style={{ position: "relative", width: "640px", height: "360px", border: "1px solid #555" }}>
|
||||
<Show when={store.mounted}>
|
||||
<SessionBrowserPane
|
||||
browser={browser}
|
||||
visible={store.visible}
|
||||
onComment={(comment) => setStore("comments", (items) => [...items, comment])}
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
<ul data-testid="fixture-comments">
|
||||
<For each={store.comments}>
|
||||
{(comment) => (
|
||||
<li>
|
||||
{comment.element.label} {comment.element.ref ? `@${comment.element.ref}` : "(no ref)"}:{" "}
|
||||
{comment.comment}
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
<Show when={store.covered}>
|
||||
{/* Floating content portals into <body> like a menu or hover card over the page. */}
|
||||
<Portal mount={document.body}>
|
||||
<div
|
||||
data-popper-positioner
|
||||
data-testid="fixture-popover"
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: "0",
|
||||
left: "0",
|
||||
width: "320px",
|
||||
height: "480px",
|
||||
"z-index": "1001",
|
||||
"pointer-events": "none",
|
||||
}}
|
||||
/>
|
||||
</Portal>
|
||||
</Show>
|
||||
<h2 style={{ "font-size": "18px", margin: "20px 0 12px" }}>Native layout recorder</h2>
|
||||
<p>The desktop boundary keeps each session's page visible until its registration is hidden.</p>
|
||||
<For each={tabs}>
|
||||
{(tab) => (
|
||||
<div
|
||||
data-testid={`native-${tab.title}`}
|
||||
data-visible={!!store.layouts[tab.title]?.visible}
|
||||
style={{ padding: "12px", margin: "8px 0", border: "1px solid #555" }}
|
||||
>
|
||||
{tab.title}: {store.layouts[tab.title]?.visible ? "visible" : "hidden"}
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return render(
|
||||
() => (
|
||||
<LanguageProvider locale="en">
|
||||
<UiI18nBridge>
|
||||
<DialogProvider>
|
||||
<Fixture />
|
||||
</DialogProvider>
|
||||
</UiI18nBridge>
|
||||
</LanguageProvider>
|
||||
),
|
||||
host,
|
||||
)
|
||||
}
|
||||
@@ -1,46 +1,49 @@
|
||||
import { fileURLToPath } from "node:url"
|
||||
import { expect, story } from "../../storybook/playwright/story"
|
||||
|
||||
const fixture = `/@fs/${fileURLToPath(new URL("./browser-pane.fixture.tsx", import.meta.url)).replaceAll("\\", "/")}`
|
||||
const source = (path: string) => `/@fs/${fileURLToPath(new URL(path, import.meta.url)).replaceAll("\\", "/")}`
|
||||
const modules = {
|
||||
fixture: source("../../gui-extensions/src/browser/panel.fixture.tsx"),
|
||||
surface: source("../src/runtime/extension/surface.tsx"),
|
||||
language: source("../src/runtime/i18n/language.tsx"),
|
||||
}
|
||||
|
||||
story.beforeEach(async ({ mount, page }) => {
|
||||
const component = await mount("opencode-composer-flow--mixed-attachments")
|
||||
await expect(component.getByRole("textbox", { name: "Prompt", exact: true })).toBeVisible()
|
||||
await page.evaluate(async (fixture) => {
|
||||
const { mountBrowserPane } = await import(fixture)
|
||||
mountBrowserPane()
|
||||
}, fixture)
|
||||
// Any story loads the app styles; the fixture mounts the pane beside it on the real host surface.
|
||||
await mount("ui-line-comment--editor")
|
||||
await page.evaluate(async (modules) => {
|
||||
const [{ mountBrowserPane }, { createSurfaces }, language] = await Promise.all([
|
||||
import(modules.fixture),
|
||||
import(modules.surface),
|
||||
import(modules.language),
|
||||
])
|
||||
mountBrowserPane({
|
||||
createSurfaces,
|
||||
LanguageProvider: language.LanguageProvider,
|
||||
UiI18nBridge: language.UiI18nBridge,
|
||||
useLanguage: language.useLanguage,
|
||||
})
|
||||
}, modules)
|
||||
await expect(page.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "true")
|
||||
})
|
||||
|
||||
story("hides the previous registration when the mounted pane switches sessions", async ({ page }, testInfo) => {
|
||||
story("hides a native page when another takes the pane, the pane hides, or it unmounts", async ({ page }) => {
|
||||
const root = page.getByTestId("browser-pane-fixture")
|
||||
const alpha = root.getByTestId("native-Alpha")
|
||||
const beta = root.getByTestId("native-Beta")
|
||||
await root.getByRole("button", { name: "Beta", exact: true }).click()
|
||||
await expect(root.getByTestId("native-Beta")).toHaveAttribute("data-visible", "true")
|
||||
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "false")
|
||||
await page.screenshot({ path: testInfo.outputPath("session-switch.png") })
|
||||
await root.getByRole("button", { name: "Alpha", exact: true }).click()
|
||||
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "true")
|
||||
await expect(root.getByTestId("native-Beta")).toHaveAttribute("data-visible", "false")
|
||||
})
|
||||
|
||||
story("hides the outgoing browser when the destination has no browser pane", async ({ page }) => {
|
||||
const root = page.getByTestId("browser-pane-fixture")
|
||||
await expect(beta).toHaveAttribute("data-visible", "true")
|
||||
await expect(alpha).toHaveAttribute("data-visible", "false")
|
||||
await root.getByRole("button", { name: "Empty", exact: true }).click()
|
||||
await expect(root.locator("#browser-panel")).toHaveCount(0)
|
||||
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "false")
|
||||
await expect(beta).toHaveAttribute("data-visible", "false")
|
||||
await root.getByRole("button", { name: "Alpha", exact: true }).click()
|
||||
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "true")
|
||||
})
|
||||
|
||||
story("hides and restores the same registration for Review tabs and unmount", async ({ page }) => {
|
||||
const root = page.getByTestId("browser-pane-fixture")
|
||||
await expect(alpha).toHaveAttribute("data-visible", "true")
|
||||
await root.getByRole("button", { name: "Toggle Review tab", exact: true }).click()
|
||||
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "false")
|
||||
await expect(alpha).toHaveAttribute("data-visible", "false")
|
||||
await root.getByRole("button", { name: "Toggle Review tab", exact: true }).click()
|
||||
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "true")
|
||||
await expect(alpha).toHaveAttribute("data-visible", "true")
|
||||
await root.getByRole("button", { name: "Unmount pane", exact: true }).click()
|
||||
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "false")
|
||||
await expect(alpha).toHaveAttribute("data-visible", "false")
|
||||
})
|
||||
|
||||
story("hides the native view immediately while the pane stays mounted", async ({ page }) => {
|
||||
@@ -58,7 +61,7 @@ story("hides the native view immediately while the pane stays mounted", async ({
|
||||
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "true")
|
||||
})
|
||||
|
||||
story("keeps a still of the page under floating content that covers it", async ({ page }, testInfo) => {
|
||||
story("keeps a still of the page under floating content that covers it", async ({ page }) => {
|
||||
const root = page.getByTestId("browser-pane-fixture")
|
||||
const still = root.locator("#browser-panel img")
|
||||
await root.getByRole("button", { name: "Hold capture", exact: true }).click()
|
||||
@@ -71,7 +74,6 @@ story("keeps a still of the page under floating content that covers it", async (
|
||||
await root.getByRole("button", { name: "Release capture", exact: true }).click()
|
||||
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "false")
|
||||
await expect(still).toBeVisible()
|
||||
await page.screenshot({ path: testInfo.outputPath("covered.png") })
|
||||
|
||||
await root.getByRole("button", { name: "Toggle popover", exact: true }).click()
|
||||
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "true")
|
||||
@@ -79,22 +81,26 @@ story("keeps a still of the page under floating content that covers it", async (
|
||||
await expect(root.getByText("Captures: 1", { exact: true })).toBeVisible()
|
||||
})
|
||||
|
||||
story("comments on a picked element over a still of the page", async ({ page }, testInfo) => {
|
||||
story("comments on a picked element over a still of the page", async ({ page }) => {
|
||||
const root = page.getByTestId("browser-pane-fixture")
|
||||
const picker = root.getByRole("button", { name: "Select an element to comment on", exact: true })
|
||||
await picker.click()
|
||||
await expect(picker).toHaveAttribute("aria-pressed", "true")
|
||||
await expect(root.getByText("Picker: on", { exact: true })).toBeVisible()
|
||||
await expect(root.getByRole("status")).toHaveText(
|
||||
"Click an element in the page to comment on it. Press Escape to cancel.",
|
||||
)
|
||||
|
||||
await root.getByRole("button", { name: "Pick element", exact: true }).click()
|
||||
await expect(picker).toHaveAttribute("aria-pressed", "false")
|
||||
const editor = root.locator('[data-slot="browser-comment-editor"] textarea')
|
||||
await expect(editor).toBeFocused()
|
||||
await expect(root.locator('[data-slot="browser-comment-editor"]')).toContainText("button.primary")
|
||||
// The spotlight frames the picked element in surface pixels.
|
||||
await expect(root.locator('[data-slot="browser-comment-spotlight"]')).toHaveCSS("left", "48px")
|
||||
// The native page hides behind its still so the editor can float over it.
|
||||
await expect(root.getByTestId("native-Alpha")).toHaveAttribute("data-visible", "false")
|
||||
await expect(root.locator("#browser-panel img")).toBeVisible()
|
||||
await page.screenshot({ path: testInfo.outputPath("comment.png") })
|
||||
|
||||
await editor.fill("Make this the primary colour")
|
||||
await editor.press("Enter")
|
||||
@@ -146,9 +152,14 @@ story("keeps the comment editor and its actions inside the page", async ({ page
|
||||
await editor.locator("textarea").evaluate((element) => {
|
||||
element.style.height = "900px"
|
||||
})
|
||||
const surface = await root.locator('[data-component="browser-comment"]').boundingBox()
|
||||
const box = await editor.boundingBox()
|
||||
expect(surface && box && box.y + box.height <= surface.y + surface.height).toBe(true)
|
||||
// The editor moves once it has measured its new height.
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const surface = await root.locator('[data-component="browser-comment"]').boundingBox()
|
||||
const box = await editor.boundingBox()
|
||||
return !!surface && !!box && box.y + box.height <= surface.y + surface.height
|
||||
})
|
||||
.toBe(true)
|
||||
const submit = editor.getByRole("button", { name: "Comment", exact: true })
|
||||
await submit.scrollIntoViewIfNeeded()
|
||||
await expect(submit).toBeInViewport()
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { DialogProvider } from "@opencode/ui/context/dialog"
|
||||
import type { Setup } from "@opencode/gui-extensions/sdk"
|
||||
import { createSignal } from "solid-js"
|
||||
import { render } from "solid-js/web"
|
||||
import { ExtensionHostProvider, useExtensionHost } from "../src/runtime/extension/host"
|
||||
import { LanguageProvider } from "../src/runtime/i18n/language"
|
||||
|
||||
/** Mounts the real extension host with one extension whose every renderer load settles when the test says so. */
|
||||
export function mountExtensionHost() {
|
||||
const loads: PromiseWithResolvers<{ default: Setup }>[] = []
|
||||
const [disabled, setDisabled] = createSignal<ReadonlySet<string>>(new Set())
|
||||
const state = { host: undefined as ReturnType<typeof useExtensionHost> | undefined }
|
||||
const container = document.createElement("div")
|
||||
document.body.appendChild(container)
|
||||
function Capture() {
|
||||
state.host = useExtensionHost()
|
||||
return null
|
||||
}
|
||||
const dispose = render(
|
||||
() => (
|
||||
<LanguageProvider locale="en">
|
||||
<DialogProvider>
|
||||
<ExtensionHostProvider
|
||||
definitions={[
|
||||
{
|
||||
id: "fixture",
|
||||
renderer: () => {
|
||||
const load = Promise.withResolvers<{ default: Setup }>()
|
||||
loads.push(load)
|
||||
return load.promise
|
||||
},
|
||||
},
|
||||
]}
|
||||
disabled={disabled}
|
||||
services={[]}
|
||||
>
|
||||
<Capture />
|
||||
</ExtensionHostProvider>
|
||||
</DialogProvider>
|
||||
</LanguageProvider>
|
||||
),
|
||||
container,
|
||||
)
|
||||
return {
|
||||
unmount: () => {
|
||||
dispose()
|
||||
container.remove()
|
||||
},
|
||||
/** Resolves the nth renderer load (the first by default) with this setup. */
|
||||
load: (setup: Setup, index = 0) => loads[index].resolve({ default: setup }),
|
||||
/** Rejects the nth renderer load. */
|
||||
fail: (index: number, error: unknown) => loads[index].reject(error),
|
||||
/** Renderer loads requested so far. */
|
||||
count: () => loads.length,
|
||||
reload: () => state.host?.reload("fixture"),
|
||||
disable: () => setDisabled(new Set(["fixture"])),
|
||||
enable: () => setDisabled(new Set<string>()),
|
||||
status: () => state.host?.state.status.fixture,
|
||||
/** Contributions the host holds for a point; readable after the host unmounts. */
|
||||
entries: (point: string) => state.host?.state.entries[point]?.length ?? 0,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import { fileURLToPath } from "node:url"
|
||||
import type { Context, Dialogs } from "@opencode/gui-extensions/sdk"
|
||||
import { expect, story } from "../../storybook/playwright/story"
|
||||
|
||||
const fixture = `/@fs/${fileURLToPath(new URL("./extension-host.fixture.tsx", import.meta.url)).replaceAll("\\", "/")}`
|
||||
|
||||
story.beforeEach(async ({ mount }) => {
|
||||
// Any story loads the app; the fixture mounts the real host beside it.
|
||||
await mount("ui-line-comment--editor")
|
||||
})
|
||||
|
||||
story("an extension that finishes loading after the host unmounts is never set up", async ({ page }) => {
|
||||
const setups = await page.evaluate(async (fixture) => {
|
||||
const { mountExtensionHost } = await import(fixture)
|
||||
const host = mountExtensionHost()
|
||||
const state = { setups: 0 }
|
||||
host.unmount()
|
||||
host.load(() => void state.setups++)
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
return state.setups
|
||||
}, fixture)
|
||||
expect(setups).toBe(0)
|
||||
})
|
||||
|
||||
story("an async setup that resolves after the host unmounts releases everything it registers", async ({ page }) => {
|
||||
const result = await page.evaluate(async (fixture) => {
|
||||
const { mountExtensionHost } = await import(fixture)
|
||||
const host = mountExtensionHost()
|
||||
const started = Promise.withResolvers<void>()
|
||||
const resume = Promise.withResolvers<void>()
|
||||
const cleaned: string[] = []
|
||||
const point = { kind: "point" as const, id: "fixture-point" }
|
||||
host.load(async (ctx: Context) => {
|
||||
ctx.add(point, "before")
|
||||
started.resolve()
|
||||
await resume.promise
|
||||
ctx.add(point, "after")
|
||||
ctx.cleanup(() => void cleaned.push("registered"))
|
||||
return () => void cleaned.push("returned")
|
||||
})
|
||||
await started.promise
|
||||
const before = host.entries(point.id)
|
||||
host.unmount()
|
||||
resume.resolve()
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
return { before, after: host.entries(point.id), cleaned }
|
||||
}, fixture)
|
||||
expect(result).toEqual({ before: 1, after: 0, cleaned: ["registered", "returned"] })
|
||||
})
|
||||
|
||||
story("older loads neither set up nor fail over the replacement after reloads", async ({ page }) => {
|
||||
const result = await page.evaluate(async (fixture) => {
|
||||
const { mountExtensionHost } = await import(fixture)
|
||||
const host = mountExtensionHost()
|
||||
const setups: string[] = []
|
||||
host.reload()
|
||||
host.reload()
|
||||
host.load(() => void setups.push("first"), 0)
|
||||
host.fail(1, new Error("second"))
|
||||
await new Promise((resolve) => setTimeout(resolve, 20))
|
||||
host.load(() => void setups.push("third"), 2)
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
const outcome = { setups, status: host.status() }
|
||||
host.unmount()
|
||||
return outcome
|
||||
}, fixture)
|
||||
expect(result).toEqual({ setups: ["third"], status: "active" })
|
||||
})
|
||||
|
||||
story("a dialog service kept from before a reload opens and closes nothing under the replacement", async ({ page }) => {
|
||||
const result = await page.evaluate(async (fixture) => {
|
||||
const { mountExtensionHost } = await import(fixture)
|
||||
const host = mountExtensionHost()
|
||||
const services: Dialogs[] = []
|
||||
const setup = (ctx: Context) => void services.push(ctx.use({ kind: "host" as const, id: "dialog" }) as Dialogs)
|
||||
const text = (value: string) => () => Object.assign(document.createElement("p"), { textContent: value })
|
||||
const shown = (value: string) => !!document.body.textContent?.includes(value)
|
||||
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
host.load(setup, 0)
|
||||
await wait(20)
|
||||
host.reload()
|
||||
host.load(setup, 1)
|
||||
await wait(20)
|
||||
const [stale, fresh] = services
|
||||
stale.push(text("stale dialog"))
|
||||
fresh.push(text("fresh dialog"))
|
||||
await wait(50)
|
||||
stale.close()
|
||||
await wait(300)
|
||||
const outcome = { stale: shown("stale dialog"), fresh: shown("fresh dialog") }
|
||||
host.unmount()
|
||||
return outcome
|
||||
}, fixture)
|
||||
expect(result).toEqual({ stale: false, fresh: true })
|
||||
})
|
||||
|
||||
story("a dialog pushed in the same tick as a reload never mounts", async ({ page }) => {
|
||||
const shown = await page.evaluate(async (fixture) => {
|
||||
const { mountExtensionHost } = await import(fixture)
|
||||
const host = mountExtensionHost()
|
||||
const services: Dialogs[] = []
|
||||
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
host.load((ctx: Context) => void services.push(ctx.use({ kind: "host" as const, id: "dialog" }) as Dialogs), 0)
|
||||
await wait(20)
|
||||
services[0].push(() => Object.assign(document.createElement("p"), { textContent: "same tick dialog" }))
|
||||
host.reload()
|
||||
await wait(300)
|
||||
const outcome = !!document.body.textContent?.includes("same tick dialog")
|
||||
host.unmount()
|
||||
return outcome
|
||||
}, fixture)
|
||||
expect(shown).toBe(false)
|
||||
})
|
||||
|
||||
story("an extension reloaded while disabled starts when it is enabled again", async ({ page }) => {
|
||||
const result = await page.evaluate(async (fixture) => {
|
||||
const { mountExtensionHost } = await import(fixture)
|
||||
const host = mountExtensionHost()
|
||||
const setups: string[] = []
|
||||
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
host.load(() => void setups.push("first"), 0)
|
||||
await wait(20)
|
||||
host.disable()
|
||||
await wait(20)
|
||||
host.reload()
|
||||
const afterReload = host.status()
|
||||
// A load the reload started finishes while the extension is still disabled.
|
||||
if (host.count() > 1) host.load(() => void setups.push("while disabled"), 1)
|
||||
await wait(20)
|
||||
host.enable()
|
||||
await wait(20)
|
||||
host.load(() => void setups.push("enabled"), host.count() - 1)
|
||||
await wait(50)
|
||||
const outcome = { afterReload, setups, status: host.status() }
|
||||
host.unmount()
|
||||
return outcome
|
||||
}, fixture)
|
||||
expect(result).toEqual({ afterReload: "disabled", setups: ["first", "enabled"], status: "active" })
|
||||
})
|
||||
@@ -1,515 +0,0 @@
|
||||
# CreateEffect Simplification Implementation Spec
|
||||
|
||||
Reduce reactive misuse across `packages/app`.
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
This work targets `packages/app/src`, which currently has 101 `createEffect` calls across 37 files.
|
||||
|
||||
The biggest clusters are `pages/session.tsx` (19), `pages/layout.tsx` (13), `pages/session/file-tabs.tsx` (6), and several context providers that mirror one store into another.
|
||||
|
||||
Key issues from the audit:
|
||||
|
||||
- Derived state is being written through effects instead of computed directly
|
||||
- Session and file resets are handled by watch-and-clear effects instead of keyed state boundaries
|
||||
- User-driven actions are hidden inside reactive effects
|
||||
- Context layers mirror and hydrate child stores with multiple sync effects
|
||||
- Several areas repeat the same imperative trigger pattern in multiple effects
|
||||
|
||||
Keep the implementation focused on removing unnecessary effects, not on broad UI redesign.
|
||||
|
||||
## Goals
|
||||
|
||||
- Cut high-churn `createEffect` usage in the hottest files first
|
||||
- Replace effect-driven derived state with reactive derivation
|
||||
- Replace reset-on-key effects with keyed ownership boundaries
|
||||
- Move event-driven work to direct actions and write paths
|
||||
- Remove mirrored store hydration where a single source of truth can exist
|
||||
- Leave necessary external sync effects in place, but make them narrower and clearer
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Do not rewrite unrelated component structure just to reduce the count
|
||||
- Do not change product behavior, navigation flow, or persisted data shape unless required for a cleaner write boundary
|
||||
- Do not remove effects that bridge to DOM, editors, polling, or external APIs unless there is a clearly safer equivalent
|
||||
- Do not attempt a repo-wide cleanup outside `packages/app`
|
||||
|
||||
## Effect Taxonomy And Replacement Rules
|
||||
|
||||
Use these rules during implementation.
|
||||
|
||||
### Prefer `createMemo`
|
||||
|
||||
Use `createMemo` when the target value is pure derived state from other signals or stores.
|
||||
|
||||
Do this when an effect only reads reactive inputs and writes another reactive value that could be computed instead.
|
||||
|
||||
Apply this to:
|
||||
|
||||
- `packages/app/src/pages/session.tsx:141`
|
||||
- `packages/app/src/pages/layout.tsx:557`
|
||||
- `packages/app/src/components/terminal.tsx:261`
|
||||
- `packages/app/src/components/session/session-header.tsx:309`
|
||||
|
||||
Rules:
|
||||
|
||||
- If no external system is touched, do not use `createEffect`
|
||||
- Derive once, then read the memo where needed
|
||||
- If normalization is required, prefer normalizing at the write boundary before falling back to a memo
|
||||
|
||||
### Prefer Keyed Remounts
|
||||
|
||||
Use keyed remounts when local UI state should reset because an identity changed.
|
||||
|
||||
Do this with `sessionKey`, `scope()`, or another stable identity instead of watching the key and manually clearing signals.
|
||||
|
||||
Apply this to:
|
||||
|
||||
- `packages/app/src/pages/session.tsx:325`
|
||||
- `packages/app/src/pages/session.tsx:336`
|
||||
- `packages/app/src/pages/session.tsx:477`
|
||||
- `packages/app/src/pages/session.tsx:869`
|
||||
- `packages/app/src/pages/session.tsx:963`
|
||||
- `packages/app/src/pages/session/message-timeline.tsx:149`
|
||||
- `packages/app/src/context/file.tsx:100`
|
||||
|
||||
Rules:
|
||||
|
||||
- If the desired behavior is "new identity, fresh local state," key the owner subtree
|
||||
- Keep state local to the keyed boundary so teardown and recreation handle the reset naturally
|
||||
|
||||
### Prefer Event Handlers And Actions
|
||||
|
||||
Use direct handlers, store actions, and async command functions when work happens because a user clicked, selected, reloaded, or navigated.
|
||||
|
||||
Do this when an effect is just watching for a flag change, command token, or event-bus signal to trigger imperative logic.
|
||||
|
||||
Apply this to:
|
||||
|
||||
- `packages/app/src/pages/layout.tsx:484`
|
||||
- `packages/app/src/pages/layout.tsx:652`
|
||||
- `packages/app/src/pages/layout.tsx:776`
|
||||
- `packages/app/src/pages/layout.tsx:1489`
|
||||
- `packages/app/src/pages/layout.tsx:1519`
|
||||
- `packages/app/src/components/file-tree.tsx:328`
|
||||
- `packages/app/src/pages/session/terminal-panel.tsx:55`
|
||||
- `packages/app/src/context/global-sync.tsx:148`
|
||||
- Duplicated trigger sets in:
|
||||
- `packages/app/src/pages/session/review-tab.tsx:122`
|
||||
- `packages/app/src/pages/session/review-tab.tsx:130`
|
||||
- `packages/app/src/pages/session/review-tab.tsx:138`
|
||||
- `packages/app/src/pages/session/file-tabs.tsx:367`
|
||||
- `packages/app/src/pages/session/file-tabs.tsx:378`
|
||||
- `packages/app/src/pages/session/file-tabs.tsx:389`
|
||||
- `packages/app/src/pages/session/use-session-hash-scroll.ts:144`
|
||||
- `packages/app/src/pages/session/use-session-hash-scroll.ts:149`
|
||||
- `packages/app/src/pages/session/use-session-hash-scroll.ts:167`
|
||||
|
||||
Rules:
|
||||
|
||||
- If the trigger is user intent, call the action at the source of that intent
|
||||
- If the same imperative work is triggered from multiple places, extract one function and call it directly
|
||||
|
||||
### Prefer `onMount` And `onCleanup`
|
||||
|
||||
Use `onMount` and `onCleanup` for lifecycle-only setup and teardown.
|
||||
|
||||
This is the right fit for subscriptions, one-time wiring, timers, and imperative integration that should not rerun for ordinary reactive changes.
|
||||
|
||||
Use this when:
|
||||
|
||||
- Setup should happen once per owner lifecycle
|
||||
- Cleanup should always pair with teardown
|
||||
- The work is not conceptually derived state
|
||||
|
||||
### Keep `createEffect` When It Is A Real Bridge
|
||||
|
||||
Keep `createEffect` when it synchronizes reactive data to an external imperative sink.
|
||||
|
||||
Examples that should remain, though they may be narrowed or split:
|
||||
|
||||
- DOM/editor sync in `packages/app/src/components/prompt-input.tsx:690`
|
||||
- Scroll sync in `packages/app/src/pages/session.tsx:685`
|
||||
- Scroll/hash sync in `packages/app/src/pages/session/use-session-hash-scroll.ts:149`
|
||||
- External sync in:
|
||||
- `packages/app/src/context/language.tsx:207`
|
||||
- `packages/app/src/context/settings.tsx:110`
|
||||
- `packages/app/src/context/sdk.tsx:26`
|
||||
- Polling in:
|
||||
- `packages/app/src/components/status-popover.tsx:59`
|
||||
- `packages/app/src/components/dialog-select-server.tsx:273`
|
||||
|
||||
Rules:
|
||||
|
||||
- Keep the effect single-purpose
|
||||
- Make dependencies explicit and narrow
|
||||
- Avoid writing back into the same reactive graph unless absolutely required
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 0: Classification Pass
|
||||
|
||||
Before changing code, tag each targeted effect as one of: derive, reset, event, lifecycle, or external bridge.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Every targeted effect in this spec is tagged with a replacement strategy before refactoring starts
|
||||
- Shared helpers to be introduced are identified up front to avoid repeating patterns
|
||||
|
||||
### Phase 1: Derived-State Cleanup
|
||||
|
||||
Tackle highest-value, lowest-risk derived-state cleanup first.
|
||||
|
||||
Priority items:
|
||||
|
||||
- Normalize tabs at write boundaries and remove `packages/app/src/pages/session.tsx:141`
|
||||
- Stop syncing `workspaceOrder` in `packages/app/src/pages/layout.tsx:557`
|
||||
- Make prompt slash filtering reactive so `packages/app/src/components/prompt-input.tsx:652` can be removed
|
||||
- Replace other obvious derived-state effects in terminal and session header
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- No behavior change in tab ordering, prompt filtering, terminal display, or header state
|
||||
- Targeted derived-state effects are deleted, not just moved
|
||||
|
||||
### Phase 2: Keyed Reset Cleanup
|
||||
|
||||
Replace reset-on-key effects with keyed ownership boundaries.
|
||||
|
||||
Priority items:
|
||||
|
||||
- Key session-scoped UI and state by `sessionKey`
|
||||
- Key file-scoped state by `scope()`
|
||||
- Remove manual clear-and-reseed effects in session and file context
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Switching session or file scope recreates the intended local state cleanly
|
||||
- No stale state leaks across session or scope changes
|
||||
- Target reset effects are deleted
|
||||
|
||||
### Phase 3: Event-Driven Work Extraction
|
||||
|
||||
Move event-driven work out of reactive effects.
|
||||
|
||||
Priority items:
|
||||
|
||||
- Replace `globalStore.reload` effect dispatching with direct calls
|
||||
- Split mixed-responsibility effect in `packages/app/src/pages/layout.tsx:1489`
|
||||
- Collapse duplicated imperative trigger triplets into single functions
|
||||
- Move file-tree and terminal-panel imperative work to explicit handlers
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- User-triggered behavior still fires exactly once per intended action
|
||||
- No effect remains whose only job is to notice a command-like state and trigger an imperative function
|
||||
|
||||
### Phase 4: Context Ownership Cleanup
|
||||
|
||||
Remove mirrored child-store hydration patterns.
|
||||
|
||||
Priority items:
|
||||
|
||||
- Remove child-store hydration mirrors in `packages/app/src/context/global-sync/child-store.ts:184`, `:190`, `:193`
|
||||
- Simplify mirror logic in `packages/app/src/context/global-sync.tsx:130`, `:138`
|
||||
- Revisit `packages/app/src/context/layout.tsx:424` if it still mirrors instead of deriving
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- There is one clear source of truth for each synced value
|
||||
- Child stores no longer need effect-based hydration to stay consistent
|
||||
- Initialization and updates both work without manual mirror effects
|
||||
|
||||
### Phase 5: Cleanup And Keeper Review
|
||||
|
||||
Clean up remaining targeted hotspots and narrow the effects that should stay.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Remaining `createEffect` calls in touched files are all true bridges or clearly justified lifecycle sync
|
||||
- Mixed-responsibility effects are split into smaller units where still needed
|
||||
|
||||
## Detailed Work Items By Area
|
||||
|
||||
### 1. Normalize Tab State
|
||||
|
||||
Files:
|
||||
|
||||
- `packages/app/src/pages/session.tsx:141`
|
||||
|
||||
Work:
|
||||
|
||||
- Move tab normalization into the functions that create, load, or update tab state
|
||||
- Make readers consume already-normalized tab data
|
||||
- Remove the effect that rewrites derived tab state after the fact
|
||||
|
||||
Rationale:
|
||||
|
||||
- Tabs should become valid when written, not be repaired later
|
||||
- This removes a feedback loop and makes state easier to trust
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- The effect at `packages/app/src/pages/session.tsx:141` is removed
|
||||
- Newly created and restored tabs are normalized before they enter local state
|
||||
- Tab rendering still matches current behavior for valid and edge-case inputs
|
||||
|
||||
### 2. Key Session-Owned State
|
||||
|
||||
Files:
|
||||
|
||||
- `packages/app/src/pages/session.tsx:325`
|
||||
- `packages/app/src/pages/session.tsx:336`
|
||||
- `packages/app/src/pages/session.tsx:477`
|
||||
- `packages/app/src/pages/session.tsx:869`
|
||||
- `packages/app/src/pages/session.tsx:963`
|
||||
- `packages/app/src/pages/session/message-timeline.tsx:149`
|
||||
|
||||
Work:
|
||||
|
||||
- Identify state that should reset when `sessionKey` changes
|
||||
- Move that state under a keyed subtree or keyed owner boundary
|
||||
- Remove effects that watch `sessionKey` just to clear local state, refs, or temporary UI flags
|
||||
|
||||
Rationale:
|
||||
|
||||
- Session identity already defines the lifetime of this UI state
|
||||
- Keyed ownership makes reset behavior automatic and easier to reason about
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- The targeted reset effects are removed
|
||||
- Changing sessions resets only the intended session-local state
|
||||
- Scroll and editor state that should persist are not accidentally reset
|
||||
|
||||
### 3. Derive Workspace Order
|
||||
|
||||
Files:
|
||||
|
||||
- `packages/app/src/pages/layout.tsx:557`
|
||||
|
||||
Work:
|
||||
|
||||
- Stop writing `workspaceOrder` from live workspace data in an effect
|
||||
- Represent user overrides separately from live workspace data
|
||||
- Compute effective order from current data plus overrides with a memo or pure helper
|
||||
|
||||
Rationale:
|
||||
|
||||
- Persisted user intent and live source data should not mirror each other through an effect
|
||||
- A computed effective order avoids drift and racey resync behavior
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- The effect at `packages/app/src/pages/layout.tsx:557` is removed
|
||||
- Workspace order updates correctly when workspaces appear, disappear, or are reordered by the user
|
||||
- User overrides persist without requiring a sync-back effect
|
||||
|
||||
### 4. Remove Child-Store Mirrors
|
||||
|
||||
Files:
|
||||
|
||||
- `packages/app/src/context/global-sync.tsx:130`
|
||||
- `packages/app/src/context/global-sync.tsx:138`
|
||||
- `packages/app/src/context/global-sync.tsx:148`
|
||||
- `packages/app/src/context/global-sync/child-store.ts:184`
|
||||
- `packages/app/src/context/global-sync/child-store.ts:190`
|
||||
- `packages/app/src/context/global-sync/child-store.ts:193`
|
||||
- `packages/app/src/context/layout.tsx:424`
|
||||
|
||||
Work:
|
||||
|
||||
- Trace the actual ownership of global and child store values
|
||||
- Replace hydration and mirror effects with explicit initialization and direct updates
|
||||
- Remove the `globalStore.reload` event-bus pattern and call the needed reload paths directly
|
||||
|
||||
Rationale:
|
||||
|
||||
- Mirrors make it hard to tell which state is authoritative
|
||||
- Event-bus style state toggles hide control flow and create accidental reruns
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Child store hydration no longer depends on effect-based copying
|
||||
- Reload work can be followed from the event source to the handler without a reactive relay
|
||||
- State remains correct on first load, child creation, and subsequent updates
|
||||
|
||||
### 5. Key File-Scoped State
|
||||
|
||||
Files:
|
||||
|
||||
- `packages/app/src/context/file.tsx:100`
|
||||
|
||||
Work:
|
||||
|
||||
- Move file-scoped local state under a boundary keyed by `scope()`
|
||||
- Remove any effect that watches `scope()` only to reset file-local state
|
||||
|
||||
Rationale:
|
||||
|
||||
- File scope changes are identity changes
|
||||
- Keyed ownership gives a cleaner reset than manual clear logic
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- The effect at `packages/app/src/context/file.tsx:100` is removed
|
||||
- Switching scopes resets only scope-local state
|
||||
- No previous-scope data appears after a scope change
|
||||
|
||||
### 6. Split Layout Side Effects
|
||||
|
||||
Files:
|
||||
|
||||
- `packages/app/src/pages/layout.tsx:1489`
|
||||
- Related event-driven effects near `packages/app/src/pages/layout.tsx:484`, `:652`, `:776`, `:1519`
|
||||
|
||||
Work:
|
||||
|
||||
- Break the mixed-responsibility effect at `:1489` into direct actions and smaller bridge effects only where required
|
||||
- Move user-triggered branches into the actual command or handler that causes them
|
||||
- Remove any branch that only exists because one effect is handling unrelated concerns
|
||||
|
||||
Rationale:
|
||||
|
||||
- Mixed effects hide cause and make reruns hard to predict
|
||||
- Smaller units reduce accidental coupling and make future cleanup safer
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- The effect at `packages/app/src/pages/layout.tsx:1489` no longer mixes unrelated responsibilities
|
||||
- Event-driven branches execute from direct handlers
|
||||
- Remaining effects in this area each have one clear external sync purpose
|
||||
|
||||
### 7. Remove Duplicate Triggers
|
||||
|
||||
Files:
|
||||
|
||||
- `packages/app/src/pages/session/review-tab.tsx:122`
|
||||
- `packages/app/src/pages/session/review-tab.tsx:130`
|
||||
- `packages/app/src/pages/session/review-tab.tsx:138`
|
||||
- `packages/app/src/pages/session/file-tabs.tsx:367`
|
||||
- `packages/app/src/pages/session/file-tabs.tsx:378`
|
||||
- `packages/app/src/pages/session/file-tabs.tsx:389`
|
||||
- `packages/app/src/pages/session/use-session-hash-scroll.ts:144`
|
||||
- `packages/app/src/pages/session/use-session-hash-scroll.ts:149`
|
||||
- `packages/app/src/pages/session/use-session-hash-scroll.ts:167`
|
||||
|
||||
Work:
|
||||
|
||||
- Extract one explicit imperative function per behavior
|
||||
- Call that function from each source event instead of replicating the same effect pattern multiple times
|
||||
- Preserve the scroll-sync effect that is truly syncing with the DOM, but remove duplicate trigger scaffolding around it
|
||||
|
||||
Rationale:
|
||||
|
||||
- Duplicate triggers make it easy to miss a case or fire twice
|
||||
- One named action is easier to test and reason about
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Repeated imperative effect triplets are collapsed into shared functions
|
||||
- Scroll behavior still works, including hash-based navigation
|
||||
- No duplicate firing is introduced
|
||||
|
||||
### 8. Make Prompt Filtering Reactive
|
||||
|
||||
Files:
|
||||
|
||||
- `packages/app/src/components/prompt-input.tsx:652`
|
||||
- Keep `packages/app/src/components/prompt-input.tsx:690` as needed
|
||||
|
||||
Work:
|
||||
|
||||
- Convert slash filtering into a pure reactive derivation from the current input and candidate command list
|
||||
- Keep only the editor or DOM bridge effect if it is still needed for imperative syncing
|
||||
|
||||
Rationale:
|
||||
|
||||
- Filtering is classic derived state
|
||||
- It should not need an effect if it can be computed from current inputs
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- The effect at `packages/app/src/components/prompt-input.tsx:652` is removed
|
||||
- Filtered slash-command results update correctly as the input changes
|
||||
- The editor sync effect at `:690` still behaves correctly
|
||||
|
||||
### 9. Clean Up Smaller Derived-State Cases
|
||||
|
||||
Files:
|
||||
|
||||
- `packages/app/src/components/terminal.tsx:261`
|
||||
- `packages/app/src/components/session/session-header.tsx:309`
|
||||
|
||||
Work:
|
||||
|
||||
- Replace effect-written local state with memos or inline derivation
|
||||
- Remove intermediate setters when the value can be computed directly
|
||||
|
||||
Rationale:
|
||||
|
||||
- These are low-risk wins that reinforce the same pattern
|
||||
- They also help keep follow-up cleanup consistent
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Targeted effects are removed
|
||||
- UI output remains unchanged under the same inputs
|
||||
|
||||
## Verification And Regression Checks
|
||||
|
||||
Run focused checks after each phase, not only at the end.
|
||||
|
||||
### Suggested Verification
|
||||
|
||||
- Switch between sessions rapidly and confirm local session UI resets only where intended
|
||||
- Open, close, and reorder tabs and confirm order and normalization remain stable
|
||||
- Change workspaces, reload workspace data, and verify effective ordering is correct
|
||||
- Change file scope and confirm stale file state does not bleed across scopes
|
||||
- Trigger layout actions that previously depended on effects and confirm they still fire once
|
||||
- Use slash commands in the prompt and verify filtering updates as you type
|
||||
- Test review tab, file tab, and hash-scroll flows for duplicate or missing triggers
|
||||
- Verify global sync initialization, reload, and child-store creation paths
|
||||
|
||||
### Regression Checks
|
||||
|
||||
- No accidental infinite reruns
|
||||
- No double-firing network or command actions
|
||||
- No lost cleanup for listeners, timers, or scroll handlers
|
||||
- No preserved stale state after identity changes
|
||||
- No removed effect that was actually bridging to DOM or an external API
|
||||
|
||||
If available, add or update tests around pure helpers introduced during this cleanup.
|
||||
|
||||
Favor tests for derived ordering, normalization, and action extraction, since those are easiest to lock down.
|
||||
|
||||
## Definition Of Done
|
||||
|
||||
This work is done when all of the following are true:
|
||||
|
||||
- The highest-leverage targets in this spec are implemented
|
||||
- Each removed effect has been replaced by a clearer pattern: memo, keyed boundary, direct action, or lifecycle hook
|
||||
- The "should remain" effects still exist only where they serve a real external sync purpose
|
||||
- Touched files have fewer mixed-responsibility effects and clearer ownership of state
|
||||
- Manual verification covers session switching, file scope changes, workspace ordering, prompt filtering, and reload flows
|
||||
- No behavior regressions are found in the targeted areas
|
||||
|
||||
A reduced raw `createEffect` count is helpful, but it is not the main success metric.
|
||||
|
||||
The main success metric is clearer ownership and fewer effect-driven state repairs.
|
||||
|
||||
## Risks And Rollout Notes
|
||||
|
||||
Main risks:
|
||||
|
||||
- Keyed remounts can reset too much if state boundaries are drawn too high
|
||||
- Store mirror removal can break initialization order if ownership is not mapped first
|
||||
- Moving event work out of effects can accidentally skip triggers that were previously implicit
|
||||
|
||||
Rollout notes:
|
||||
|
||||
- Land in small phases, with each phase keeping the app behaviorally stable
|
||||
- Prefer isolated PRs by phase or by file cluster, especially for context-store changes
|
||||
- Review each remaining effect in touched files and leave it only if it clearly bridges to something external
|
||||
@@ -14,3 +14,11 @@
|
||||
- Do not retry state-changing actions. Retry idempotent readiness checks, then perform the action once and assert its outcome.
|
||||
- Keep action and assertion timeouts adaptive. Do not use short timeouts as readiness probes or rely on retries to hide flakes.
|
||||
- Assert exact outcomes and identities so stale state, duplicate rendering, and interactions with the wrong element cannot pass.
|
||||
|
||||
## Test Ownership
|
||||
|
||||
- Each product area has one keeper suite in `regression/` (for example tabs, settings, review, terminal, timeline history). A new regression is a new case in that suite, not a new spec file.
|
||||
- Build pages with the shared harness in `utils/` (`app.ts`, `workspace.ts`, `mock-server.ts` with `mockServers` and the mock PTY). Do not hand-write routing, storage seeding, or fixtures inside a spec. If the harness lacks something, add it to `utils/` once rather than copying setup.
|
||||
- Files in `utils/` must not end in `.spec.ts` or `.test.ts`, or Playwright runs them as tests.
|
||||
- Assert what the user sees and can do. Assert exact pixels or CSS values only when that measurement is the contract, and then only once, in the owning keeper.
|
||||
- `performance/` holds benchmarks that CI does not run. Never prove correctness with a benchmark, and never copy benchmark code into a regression spec; import it from `utils/`.
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { SessionMessageInfo } from "@opencode/client/promise"
|
||||
import { benchmark, expect } from "../benchmark"
|
||||
import { mockOpenCodeServer } from "../../utils/mock-server"
|
||||
import { fixture } from "../timeline/session-timeline-stress.fixture"
|
||||
import { installStressSessionTabs, installTimelineSettings, stressSessionHref } from "../timeline/timeline-test-helpers"
|
||||
import { fixture, installStressSessionTabs, installTimelineSettings } from "../../utils/session-fixture"
|
||||
import { sessionHref } from "../../utils/app"
|
||||
import { completedAnswer } from "../../../../session-ui/performance/markdown-lifetime/answer"
|
||||
import { installMarkdownGate } from "./probe"
|
||||
|
||||
@@ -54,22 +54,22 @@ for (const size of ["typical", "large"]) {
|
||||
await installStressSessionTabs(page)
|
||||
const targetPart = `msg_2_${fixture.targetID}_assistant:text:0`
|
||||
const sourcePart = `msg_2_${fixture.sourceID}_assistant:text:0`
|
||||
await installMarkdownGate(page, { answer, targetPart, sourcePart, href: stressSessionHref(fixture.sourceID) })
|
||||
await installMarkdownGate(page, { answer, targetPart, sourcePart, href: sessionHref(fixture.sourceID) })
|
||||
const prefetched = page.waitForResponse((response) =>
|
||||
new URL(response.url()).pathname.endsWith(`/session/${fixture.targetID}/message`),
|
||||
)
|
||||
await page.goto(stressSessionHref(fixture.sourceID))
|
||||
await page.goto(sessionHref(fixture.sourceID))
|
||||
await prefetched
|
||||
const source = page.locator(`[data-timeline-part-id="${sourcePart}"] [data-component="markdown"]`)
|
||||
await expect(source).toHaveAttribute("data-markdown-ready", "")
|
||||
await page.locator(`[data-slot="titlebar-tabs"] a[href="${stressSessionHref(fixture.targetID)}"]`).click()
|
||||
await page.locator(`[data-slot="titlebar-tabs"] a[href="${sessionHref(fixture.targetID)}"]`).click()
|
||||
await page.waitForFunction(() => window.markdownGate.held)
|
||||
await expect(page.locator(`[data-timeline-part-id="${targetPart}"]`)).toBeAttached()
|
||||
const cdp = await page.context().newCDPSession(page)
|
||||
await cdp.send("Performance.enable")
|
||||
const before = await cdp.send("Performance.getMetrics")
|
||||
await page.evaluate(() => window.markdownGate.arm())
|
||||
await page.locator(`[data-slot="titlebar-tabs"] a[href="${stressSessionHref(fixture.sourceID)}"]`).click()
|
||||
await page.locator(`[data-slot="titlebar-tabs"] a[href="${sessionHref(fixture.sourceID)}"]`).click()
|
||||
await expect(source).toHaveAttribute("data-markdown-ready", "")
|
||||
await expect(source.getByRole("heading", { name: "Current destination" })).toBeVisible()
|
||||
await expect(page.locator(`[data-timeline-part-id="${targetPart}"]`)).toHaveCount(0)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import path from "node:path"
|
||||
import { parseArgs } from "node:util"
|
||||
import { createMockServerHandler } from "../utils/mock-server"
|
||||
import { fixture } from "./timeline/session-timeline-stress.fixture"
|
||||
import { messages } from "./timeline/session-tab-switch.fixture"
|
||||
import { fixture } from "../utils/session-fixture"
|
||||
import { messages } from "../utils/markdown-sessions"
|
||||
import { createReviewDiffs } from "./timeline/timeline-test-helpers"
|
||||
|
||||
const args = parseArgs({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Terminal } from "ghostty-web"
|
||||
import { SerializeAddon } from "../../../src/session/terminal/serialize"
|
||||
import { SerializeAddon } from "../../../../gui-extensions/src/terminal/serialize"
|
||||
|
||||
export type TerminalProbe = {
|
||||
term?: Terminal
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
userMessage,
|
||||
waitForVisualSettle,
|
||||
type TimelineMessage,
|
||||
} from "./fixture"
|
||||
} from "../../utils/timeline"
|
||||
|
||||
test.describe("timeline adverse visual stability", () => {
|
||||
test("does not pull a scrolled-away user while an active shell grows", async ({ page }, testInfo) => {
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
textPart,
|
||||
userMessage,
|
||||
waitForVisualSettle,
|
||||
} from "./fixture"
|
||||
} from "../../utils/timeline"
|
||||
|
||||
// Fractional scaling exercises different browser rounding than the baseline.
|
||||
for (const deviceScaleFactor of [1, 1.25]) {
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
toolPart,
|
||||
userMessage,
|
||||
waitForVisualSettle,
|
||||
} from "./fixture"
|
||||
} from "../../utils/timeline"
|
||||
|
||||
const profiles = [
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
toolPart,
|
||||
userMessage,
|
||||
waitForVisualSettle,
|
||||
} from "./fixture"
|
||||
} from "../../utils/timeline"
|
||||
|
||||
test("adds patch files incrementally without resetting outer expansion", async ({ page }, testInfo) => {
|
||||
const patchID = "prt_incremental_01_patch"
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
validateTimelineEvent,
|
||||
validateTimelineMessages,
|
||||
type PartSeed,
|
||||
} from "./fixture"
|
||||
} from "../../utils/timeline"
|
||||
|
||||
describe("timeline fixture validation", () => {
|
||||
test("accepts a valid timeline", () => {
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
toolPart,
|
||||
userMessage,
|
||||
waitForVisualSettle,
|
||||
} from "./fixture"
|
||||
} from "../../utils/timeline"
|
||||
|
||||
test("expands and collapses a long completed shell without overlap", async ({ page }, testInfo) => {
|
||||
const shellID = "prt_interaction_01_shell"
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
textPart,
|
||||
userMessage,
|
||||
waitForVisualSettle,
|
||||
} from "./fixture"
|
||||
} from "../../utils/timeline"
|
||||
|
||||
test.describe("timeline visual lifecycle stability", () => {
|
||||
test("streams empty, short, and long parallel shells to staggered completion", async ({ page }, testInfo) => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
stopVisualProbe,
|
||||
visualPlan,
|
||||
} from "../../utils/visual-stability"
|
||||
import { assistantMessage, renderedPartID, setupTimeline, textPart, userMessage } from "./fixture"
|
||||
import { assistantMessage, renderedPartID, setupTimeline, textPart, userMessage } from "../../utils/timeline"
|
||||
|
||||
test("detects blanking caused by ancestor opacity", async ({ page }) => {
|
||||
const partID = "prt_oracle_ancestor_opacity"
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
toolPart,
|
||||
userMessage,
|
||||
type TimelineMessage,
|
||||
} from "./fixture"
|
||||
} from "../../utils/timeline"
|
||||
|
||||
test("follows an expanded patch that arrives as the user reaches the bottom", async ({ page }) => {
|
||||
const toolID = "prt_bottom_follow_patch"
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
textPart,
|
||||
userMessage,
|
||||
waitForVisualSettle,
|
||||
} from "./fixture"
|
||||
} from "../../utils/timeline"
|
||||
|
||||
const profiles = [
|
||||
{
|
||||
|
||||
Loaded 100 of 1816 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user