Compare commits
111 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e3ca654ae4 | |||
| 8b69f05cc0 | |||
| da365455cc | |||
| 24c454a3d4 | |||
| 450957a64a | |||
| 2e6fdf2344 | |||
| 3d139c1a24 | |||
| f957981c8b | |||
| 584c61acb5 | |||
| f95a5202bf | |||
| d954460380 | |||
| f4240bb8c1 | |||
| 7caef4987f | |||
| 9b91af8ab7 | |||
| 506fba9ebf | |||
| 461a31e5d8 | |||
| e3a0dcd6fc | |||
| 12ada0115f | |||
| 7b0421d8c6 | |||
| 477d6d0cde | |||
| de61ac529d | |||
| 7f496c2f18 | |||
| 590235138f | |||
| 543c448b72 | |||
| 35c26ce22a | |||
| c2592c9f12 | |||
| b969d66978 | |||
| 1f808cdc62 | |||
| 497f637f58 | |||
| be686a8d0d | |||
| 7b7baea851 | |||
| a3de0fc78d | |||
| ab77154975 | |||
| c5d720f73d | |||
| 983819860f | |||
| f820945d9f | |||
| da5395a8ae | |||
| 86b919e4f7 | |||
| 233343c010 | |||
| 3a612898c6 | |||
| 3f7a3e3354 | |||
| 17a71aea72 | |||
| 7d3c5ac78c | |||
| d87c5ef439 | |||
| ef3e17317c | |||
| 1187b84c54 | |||
| 45336bd9ce | |||
| 36cf7debce | |||
| 027c222e82 | |||
| d00f148b76 | |||
| 48bc215f20 | |||
| 08bd9e672a | |||
| c5f1a2fccf | |||
| 655d647d40 | |||
| f3f0a2c553 | |||
| 5453a6c6d6 | |||
| 21c08a37d7 | |||
| 2bd261fbbf | |||
| cffc68df58 | |||
| 6e5335df1e | |||
| 16265e834e | |||
| 565a15bc9b | |||
| 76a2cb1af5 | |||
| 684f21740d | |||
| 86ca61d6ca | |||
| f0521cfa3c | |||
| 0c5d189aff | |||
| d7c8213eee | |||
| 63924ac687 | |||
| 6c47e9ea23 | |||
| aede045549 | |||
| 2ea08aa168 | |||
| d4b9248202 | |||
| fd6c623560 | |||
| 99da498679 | |||
| a20b817c28 | |||
| d5a1792f07 | |||
| beb99c17de | |||
| 8913f9f5c1 | |||
| acd5f01b4b | |||
| 6c62d8f5e6 | |||
| 5000f15021 | |||
| 44be5a0d1e | |||
| 3ca2aae9ca | |||
| 01080302a5 | |||
| 61d6c728b9 | |||
| 648787e739 | |||
| d2950e7615 | |||
| df8f695e84 | |||
| 8bb48ffdda | |||
| 4cfbf544a0 | |||
| a2ce74f398 | |||
| 0060dee222 | |||
| 5ff1058f30 | |||
| 500fc5e2a4 | |||
| baba9e6ede | |||
| b36619b905 | |||
| 937f50f0ae | |||
| a16755dd66 | |||
| 8bdc35f91f | |||
| 5b32c4d79d | |||
| 8625543413 | |||
| e18094825a | |||
| 919dc299fc | |||
| 7e63fe68f7 | |||
| a1d51f0dab | |||
| 333503f74e | |||
| 01b8cdb00d | |||
| ec83065b59 | |||
| 2a1bef18e0 | |||
| 8369fa88fd |
@@ -1,2 +0,0 @@
|
||||
enabled: true
|
||||
preservePullRequestTitle: true
|
||||
@@ -0,0 +1,174 @@
|
||||
# Automatically backport merged PRs to the last N release branches when the
|
||||
# "backport" label is applied. Works whether the label is added before or
|
||||
# after the PR is merged.
|
||||
#
|
||||
# Usage:
|
||||
# 1. Add the "backport" label to a PR targeting main.
|
||||
# 2. When the PR merges (or if already merged), the workflow detects the
|
||||
# latest release/* branches and opens one cherry-pick PR per branch.
|
||||
#
|
||||
# The created backport PRs follow existing repo conventions:
|
||||
# - Branch: backport/<pr>-to-<version>
|
||||
# - Title: <original PR title> (#<pr>)
|
||||
# - Body: links back to the original PR and merge commit
|
||||
|
||||
name: Backport
|
||||
on:
|
||||
pull_request_target:
|
||||
branches:
|
||||
- main
|
||||
types:
|
||||
- closed
|
||||
- labeled
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
# Prevent duplicate runs for the same PR when both 'closed' and 'labeled'
|
||||
# fire in quick succession.
|
||||
concurrency:
|
||||
group: backport-${{ github.event.pull_request.number }}
|
||||
|
||||
jobs:
|
||||
detect:
|
||||
name: Detect target branches
|
||||
if: >
|
||||
github.event.pull_request.merged == true &&
|
||||
contains(github.event.pull_request.labels.*.name, 'backport')
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
branches: ${{ steps.find.outputs.branches }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Need all refs to discover release branches.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Find latest release branches
|
||||
id: find
|
||||
run: |
|
||||
# List remote release branches matching the exact release/2.X
|
||||
# pattern (no suffixes like release/2.31_hotfix), sort by minor
|
||||
# version descending, and take the top 3.
|
||||
BRANCHES=$(
|
||||
git branch -r \
|
||||
| grep -E '^\s*origin/release/2\.[0-9]+$' \
|
||||
| sed 's|.*origin/||' \
|
||||
| sort -t. -k2 -n -r \
|
||||
| head -3
|
||||
)
|
||||
|
||||
if [ -z "$BRANCHES" ]; then
|
||||
echo "No release branches found."
|
||||
echo "branches=[]" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Convert to JSON array for the matrix.
|
||||
JSON=$(echo "$BRANCHES" | jq -Rnc '[inputs | select(length > 0)]')
|
||||
echo "branches=$JSON" >> "$GITHUB_OUTPUT"
|
||||
echo "Will backport to: $JSON"
|
||||
|
||||
backport:
|
||||
name: "Backport to ${{ matrix.branch }}"
|
||||
needs: detect
|
||||
if: needs.detect.outputs.branches != '[]'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
branch: ${{ fromJson(needs.detect.outputs.branches) }}
|
||||
fail-fast: false
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Full history required for cherry-pick.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cherry-pick and open PR
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
RELEASE_VERSION="${{ matrix.branch }}"
|
||||
# Strip the release/ prefix for naming.
|
||||
VERSION="${RELEASE_VERSION#release/}"
|
||||
BACKPORT_BRANCH="backport/${PR_NUMBER}-to-${VERSION}"
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Check if backport branch already exists (idempotency for re-runs).
|
||||
if git ls-remote --exit-code origin "refs/heads/${BACKPORT_BRANCH}" >/dev/null 2>&1; then
|
||||
echo "Backport branch ${BACKPORT_BRANCH} already exists, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create the backport branch from the target release branch.
|
||||
git checkout -b "$BACKPORT_BRANCH" "origin/${RELEASE_VERSION}"
|
||||
|
||||
# Cherry-pick the merge commit. Use -x to record provenance and
|
||||
# -m1 to pick the first parent (the main branch side).
|
||||
CONFLICTS=false
|
||||
if ! git cherry-pick -x -m1 "$MERGE_SHA"; then
|
||||
echo "::warning::Cherry-pick to ${RELEASE_VERSION} had conflicts."
|
||||
CONFLICTS=true
|
||||
|
||||
# Abort the failed cherry-pick and create an empty commit
|
||||
# explaining the situation.
|
||||
git cherry-pick --abort
|
||||
git commit --allow-empty -m "Cherry-pick of #${PR_NUMBER} requires manual resolution
|
||||
|
||||
The automatic cherry-pick of ${MERGE_SHA} to ${RELEASE_VERSION} had conflicts.
|
||||
Please cherry-pick manually:
|
||||
|
||||
git cherry-pick -x -m1 ${MERGE_SHA}"
|
||||
fi
|
||||
|
||||
git push origin "$BACKPORT_BRANCH"
|
||||
|
||||
TITLE="${PR_TITLE} (#${PR_NUMBER})"
|
||||
BODY=$(cat <<EOF
|
||||
Backport of ${PR_URL}
|
||||
|
||||
Original PR: #${PR_NUMBER} — ${PR_TITLE}
|
||||
Merge commit: ${MERGE_SHA}
|
||||
EOF
|
||||
)
|
||||
|
||||
if [ "$CONFLICTS" = true ]; then
|
||||
TITLE="${TITLE} (conflicts)"
|
||||
BODY="${BODY}
|
||||
|
||||
> [!WARNING]
|
||||
> The automatic cherry-pick had conflicts.
|
||||
> Please resolve manually by cherry-picking the original merge commit:
|
||||
>
|
||||
> \`\`\`
|
||||
> git fetch origin ${BACKPORT_BRANCH}
|
||||
> git checkout ${BACKPORT_BRANCH}
|
||||
> git reset --hard origin/${RELEASE_VERSION}
|
||||
> git cherry-pick -x -m1 ${MERGE_SHA}
|
||||
> # resolve conflicts, then push
|
||||
> \`\`\`"
|
||||
fi
|
||||
|
||||
# Check if a PR already exists for this branch (idempotency
|
||||
# for re-runs).
|
||||
EXISTING_PR=$(gh pr list --head "$BACKPORT_BRANCH" --base "$RELEASE_VERSION" --state all --json number --jq '.[0].number // empty')
|
||||
if [ -n "$EXISTING_PR" ]; then
|
||||
echo "PR #${EXISTING_PR} already exists for ${BACKPORT_BRANCH}, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
gh pr create \
|
||||
--base "$RELEASE_VERSION" \
|
||||
--head "$BACKPORT_BRANCH" \
|
||||
--title "$TITLE" \
|
||||
--body "$BODY"
|
||||
@@ -0,0 +1,139 @@
|
||||
# Automatically cherry-pick merged PRs to the latest release branch when the
|
||||
# "cherry-pick" label is applied. Works whether the label is added before or
|
||||
# after the PR is merged.
|
||||
#
|
||||
# Usage:
|
||||
# 1. Add the "cherry-pick" label to a PR targeting main.
|
||||
# 2. When the PR merges (or if already merged), the workflow detects the
|
||||
# latest release/* branch and opens a cherry-pick PR against it.
|
||||
#
|
||||
# The created PRs follow existing repo conventions:
|
||||
# - Branch: backport/<pr>-to-<version>
|
||||
# - Title: <original PR title> (#<pr>)
|
||||
# - Body: links back to the original PR and merge commit
|
||||
|
||||
name: Cherry-pick to release
|
||||
on:
|
||||
pull_request_target:
|
||||
branches:
|
||||
- main
|
||||
types:
|
||||
- closed
|
||||
- labeled
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
# Prevent duplicate runs for the same PR when both 'closed' and 'labeled'
|
||||
# fire in quick succession.
|
||||
concurrency:
|
||||
group: cherry-pick-${{ github.event.pull_request.number }}
|
||||
|
||||
jobs:
|
||||
cherry-pick:
|
||||
name: Cherry-pick to latest release
|
||||
if: >
|
||||
github.event.pull_request.merged == true &&
|
||||
contains(github.event.pull_request.labels.*.name, 'cherry-pick')
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Full history required for cherry-pick and branch discovery.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cherry-pick and open PR
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Find the latest release branch matching the exact release/2.X
|
||||
# pattern (no suffixes like release/2.31_hotfix).
|
||||
RELEASE_BRANCH=$(
|
||||
git branch -r \
|
||||
| grep -E '^\s*origin/release/2\.[0-9]+$' \
|
||||
| sed 's|.*origin/||' \
|
||||
| sort -t. -k2 -n -r \
|
||||
| head -1
|
||||
)
|
||||
|
||||
if [ -z "$RELEASE_BRANCH" ]; then
|
||||
echo "::error::No release branch found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Strip the release/ prefix for naming.
|
||||
VERSION="${RELEASE_BRANCH#release/}"
|
||||
BACKPORT_BRANCH="backport/${PR_NUMBER}-to-${VERSION}"
|
||||
|
||||
echo "Target branch: $RELEASE_BRANCH"
|
||||
echo "Backport branch: $BACKPORT_BRANCH"
|
||||
|
||||
# Check if backport branch already exists (idempotency for re-runs).
|
||||
if git ls-remote --exit-code origin "refs/heads/${BACKPORT_BRANCH}" >/dev/null 2>&1; then
|
||||
echo "Branch ${BACKPORT_BRANCH} already exists, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Create the backport branch from the target release branch.
|
||||
git checkout -b "$BACKPORT_BRANCH" "origin/${RELEASE_BRANCH}"
|
||||
|
||||
# Cherry-pick the merge commit. Use -x to record provenance and
|
||||
# -m1 to pick the first parent (the main branch side).
|
||||
CONFLICT=false
|
||||
if ! git cherry-pick -x -m1 "$MERGE_SHA"; then
|
||||
CONFLICT=true
|
||||
echo "::warning::Cherry-pick to ${RELEASE_BRANCH} had conflicts."
|
||||
|
||||
# Abort the failed cherry-pick and create an empty commit with
|
||||
# instructions so the PR can still be opened.
|
||||
git cherry-pick --abort
|
||||
git commit --allow-empty -m "cherry-pick of #${PR_NUMBER} failed — resolve conflicts manually
|
||||
|
||||
Cherry-pick of ${MERGE_SHA} onto ${RELEASE_BRANCH} had conflicts.
|
||||
To resolve:
|
||||
git fetch origin ${BACKPORT_BRANCH}
|
||||
git checkout ${BACKPORT_BRANCH}
|
||||
git cherry-pick -x -m1 ${MERGE_SHA}
|
||||
# resolve conflicts
|
||||
git push origin ${BACKPORT_BRANCH}"
|
||||
fi
|
||||
|
||||
git push origin "$BACKPORT_BRANCH"
|
||||
|
||||
BODY=$(cat <<EOF
|
||||
Cherry-pick of ${PR_URL}
|
||||
|
||||
Original PR: #${PR_NUMBER} — ${PR_TITLE}
|
||||
Merge commit: ${MERGE_SHA}
|
||||
EOF
|
||||
)
|
||||
|
||||
TITLE="${PR_TITLE} (#${PR_NUMBER})"
|
||||
if [ "$CONFLICT" = true ]; then
|
||||
TITLE="[CONFLICT] ${TITLE}"
|
||||
fi
|
||||
|
||||
# Check if a PR already exists for this branch (idempotency
|
||||
# for re-runs). Use --state all to catch closed/merged PRs too.
|
||||
EXISTING_PR=$(gh pr list --head "$BACKPORT_BRANCH" --base "$RELEASE_BRANCH" --state all --json number --jq '.[0].number // empty')
|
||||
if [ -n "$EXISTING_PR" ]; then
|
||||
echo "PR #${EXISTING_PR} already exists for ${BACKPORT_BRANCH}, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
gh pr create \
|
||||
--base "$RELEASE_BRANCH" \
|
||||
--head "$BACKPORT_BRANCH" \
|
||||
--title "$TITLE" \
|
||||
--body "$BODY"
|
||||
+18
-17
@@ -35,7 +35,7 @@ jobs:
|
||||
tailnet-integration: ${{ steps.filter.outputs.tailnet-integration }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -77,6 +77,7 @@ jobs:
|
||||
# Main repo directories for completeness in case other files are
|
||||
# touched:
|
||||
- "agent/**"
|
||||
- "aibridge/**"
|
||||
- "cli/**"
|
||||
- "cmd/**"
|
||||
- "coderd/**"
|
||||
@@ -157,7 +158,7 @@ jobs:
|
||||
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -247,7 +248,7 @@ jobs:
|
||||
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -272,7 +273,7 @@ jobs:
|
||||
if: ${{ !cancelled() }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -327,7 +328,7 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -379,7 +380,7 @@ jobs:
|
||||
- windows-2022
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -575,7 +576,7 @@ jobs:
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -637,7 +638,7 @@ jobs:
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -709,7 +710,7 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -736,7 +737,7 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -769,7 +770,7 @@ jobs:
|
||||
name: ${{ matrix.variant.name }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -849,7 +850,7 @@ jobs:
|
||||
if: needs.changes.outputs.site == 'true' || needs.changes.outputs.ci == 'true'
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -930,7 +931,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -1005,7 +1006,7 @@ jobs:
|
||||
if: always()
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -1043,7 +1044,7 @@ jobs:
|
||||
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -1097,7 +1098,7 @@ jobs:
|
||||
IMAGE: ghcr.io/coder/coder-preview:${{ steps.build-docker.outputs.tag }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -1479,7 +1480,7 @@ jobs:
|
||||
if: needs.changes.outputs.db == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
steps:
|
||||
- name: Dependabot metadata
|
||||
id: metadata
|
||||
uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0
|
||||
uses: dependabot/fetch-metadata@ffa630c65fa7e0ecfa0625b5ceda64399aea1b36 # v3.0.0
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
verdict: ${{ steps.check.outputs.verdict }} # DEPLOY or NOOP
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
packages: write # to retag image as dogfood
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -142,7 +142,7 @@ jobs:
|
||||
needs: deploy
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
if: github.repository_owner == 'coder'
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-4' || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
- windows-2022
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
packages: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
PR_OPEN: ${{ steps.check_pr.outputs.pr_open }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -184,7 +184,7 @@ jobs:
|
||||
pull-requests: write # needed for commenting on PRs
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -228,7 +228,7 @@ jobs:
|
||||
CODER_IMAGE_TAG: ${{ needs.get_info.outputs.CODER_IMAGE_TAG }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -288,7 +288,7 @@ jobs:
|
||||
PR_HOSTNAME: "pr${{ needs.get_info.outputs.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -121,22 +121,22 @@ jobs:
|
||||
fi
|
||||
|
||||
# Derive the release branch from the version tag.
|
||||
# Standard: 2.10.2 -> release/2.10
|
||||
# RC: 2.32.0-rc.0 -> release/2.32-rc.0
|
||||
# Non-RC releases must be on a release/X.Y branch.
|
||||
# RC tags are allowed on any branch (typically main).
|
||||
version="$(./scripts/version.sh)"
|
||||
# Strip any pre-release suffix first (e.g. 2.32.0-rc.0 -> 2.32.0)
|
||||
base_version="${version%%-*}"
|
||||
# Then strip patch to get major.minor (e.g. 2.32.0 -> 2.32)
|
||||
release_branch="release/${base_version%.*}"
|
||||
|
||||
if [[ "$version" == *-rc.* ]]; then
|
||||
# Extract major.minor and rc suffix from e.g. 2.32.0-rc.0
|
||||
base_version="${version%%-rc.*}" # 2.32.0
|
||||
major_minor="${base_version%.*}" # 2.32
|
||||
rc_suffix="${version##*-rc.}" # 0
|
||||
release_branch="release/${major_minor}-rc.${rc_suffix}"
|
||||
echo "RC release detected — skipping release branch check (RC tags are cut from main)."
|
||||
else
|
||||
release_branch=release/${version%.*}
|
||||
fi
|
||||
branch_contains_tag=$(git branch --remotes --contains "${GITHUB_REF}" --list "*/${release_branch}" --format='%(refname)')
|
||||
if [[ -z "${branch_contains_tag}" ]]; then
|
||||
echo "Ref tag must exist in a branch named ${release_branch} when creating a release, did you use scripts/release.sh?"
|
||||
exit 1
|
||||
branch_contains_tag=$(git branch --remotes --contains "${GITHUB_REF}" --list "*/${release_branch}" --format='%(refname)')
|
||||
if [[ -z "${branch_contains_tag}" ]]; then
|
||||
echo "Ref tag must exist in a branch named ${release_branch} when creating a non-RC release, did you use scripts/release.sh?"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "${CODER_RELEASE_NOTES}" ]]; then
|
||||
@@ -673,7 +673,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -749,7 +749,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -47,6 +47,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3.29.5
|
||||
uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
uses: ./.github/actions/setup-go
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3.29.5
|
||||
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
|
||||
with:
|
||||
languages: go, javascript
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
rm Makefile
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3.29.5
|
||||
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
|
||||
|
||||
- name: Send Slack notification on failure
|
||||
if: ${{ failure() }}
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
actions: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ typ = "typ"
|
||||
styl = "styl"
|
||||
edn = "edn"
|
||||
Inferrable = "Inferrable"
|
||||
IIF = "IIF"
|
||||
|
||||
[files]
|
||||
extend-exclude = [
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
pull-requests: write # required to post PR review comments by the action
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -103,3 +103,6 @@ PLAN.md
|
||||
|
||||
# Ignore any dev licenses
|
||||
license.txt
|
||||
-e
|
||||
# Agent planning documents (local working files).
|
||||
docs/plans/
|
||||
|
||||
@@ -6,6 +6,21 @@ linters-settings:
|
||||
# goal: 100
|
||||
threshold: 412
|
||||
|
||||
depguard:
|
||||
rules:
|
||||
aibridge_boundary:
|
||||
list-mode: lax
|
||||
files:
|
||||
- "aibridge/*.go"
|
||||
- "aibridge/**/*.go"
|
||||
allow:
|
||||
- $gostd
|
||||
- github.com/coder/coder/v2/aibridge
|
||||
- github.com/coder/coder/v2/buildinfo
|
||||
deny:
|
||||
- pkg: github.com/coder/coder/v2
|
||||
desc: aibridge code must not import coder packages outside aibridge; buildinfo is the only exception
|
||||
|
||||
exhaustruct:
|
||||
include:
|
||||
# Gradually extend to cover more of the codebase.
|
||||
@@ -227,6 +242,7 @@ linters:
|
||||
- asciicheck
|
||||
- bidichk
|
||||
- bodyclose
|
||||
- depguard
|
||||
- dogsled
|
||||
- errcheck
|
||||
- errname
|
||||
|
||||
+15
-7
@@ -102,6 +102,8 @@ type Options struct {
|
||||
ReportMetadataInterval time.Duration
|
||||
ServiceBannerRefreshInterval time.Duration
|
||||
BlockFileTransfer bool
|
||||
BlockReversePortForwarding bool
|
||||
BlockLocalPortForwarding bool
|
||||
Execer agentexec.Execer
|
||||
Devcontainers bool
|
||||
DevcontainerAPIOptions []agentcontainers.Option // Enable Devcontainers for these to be effective.
|
||||
@@ -214,6 +216,8 @@ func New(options Options) Agent {
|
||||
subsystems: options.Subsystems,
|
||||
logSender: agentsdk.NewLogSender(options.Logger),
|
||||
blockFileTransfer: options.BlockFileTransfer,
|
||||
blockReversePortForwarding: options.BlockReversePortForwarding,
|
||||
blockLocalPortForwarding: options.BlockLocalPortForwarding,
|
||||
|
||||
prometheusRegistry: prometheusRegistry,
|
||||
metrics: newAgentMetrics(prometheusRegistry),
|
||||
@@ -280,6 +284,8 @@ type agent struct {
|
||||
sshServer *agentssh.Server
|
||||
sshMaxTimeout time.Duration
|
||||
blockFileTransfer bool
|
||||
blockReversePortForwarding bool
|
||||
blockLocalPortForwarding bool
|
||||
|
||||
lifecycleUpdate chan struct{}
|
||||
lifecycleReported chan codersdk.WorkspaceAgentLifecycle
|
||||
@@ -331,12 +337,14 @@ func (a *agent) TailnetConn() *tailnet.Conn {
|
||||
func (a *agent) init() {
|
||||
// pass the "hard" context because we explicitly close the SSH server as part of graceful shutdown.
|
||||
sshSrv, err := agentssh.NewServer(a.hardCtx, a.logger.Named("ssh-server"), a.prometheusRegistry, a.filesystem, a.execer, &agentssh.Config{
|
||||
MaxTimeout: a.sshMaxTimeout,
|
||||
MOTDFile: func() string { return a.manifest.Load().MOTDFile },
|
||||
AnnouncementBanners: func() *[]codersdk.BannerConfig { return a.announcementBanners.Load() },
|
||||
UpdateEnv: a.updateCommandEnv,
|
||||
WorkingDirectory: func() string { return a.manifest.Load().Directory },
|
||||
BlockFileTransfer: a.blockFileTransfer,
|
||||
MaxTimeout: a.sshMaxTimeout,
|
||||
MOTDFile: func() string { return a.manifest.Load().MOTDFile },
|
||||
AnnouncementBanners: func() *[]codersdk.BannerConfig { return a.announcementBanners.Load() },
|
||||
UpdateEnv: a.updateCommandEnv,
|
||||
WorkingDirectory: func() string { return a.manifest.Load().Directory },
|
||||
BlockFileTransfer: a.blockFileTransfer,
|
||||
BlockReversePortForwarding: a.blockReversePortForwarding,
|
||||
BlockLocalPortForwarding: a.blockLocalPortForwarding,
|
||||
ReportConnection: func(id uuid.UUID, magicType agentssh.MagicSessionType, ip string) func(code int, reason string) {
|
||||
var connectionType proto.Connection_Type
|
||||
switch magicType {
|
||||
@@ -1366,7 +1374,7 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context,
|
||||
// lifecycle transition to avoid delaying Ready.
|
||||
// This runs inside the tracked goroutine so it
|
||||
// is properly awaited on shutdown.
|
||||
if mcpErr := a.mcpManager.Connect(a.gracefulCtx, a.contextConfigAPI.Config().MCPConfigFiles); mcpErr != nil {
|
||||
if mcpErr := a.mcpManager.Connect(a.gracefulCtx, a.contextConfigAPI.MCPConfigFiles()); mcpErr != nil {
|
||||
a.logger.Warn(ctx, "failed to connect to workspace MCP servers", slog.Error(mcpErr))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -83,14 +83,14 @@ func TestContextConfigAPI_InitOnce(t *testing.T) {
|
||||
return ""
|
||||
})
|
||||
|
||||
cfg1 := a.contextConfigAPI.Config()
|
||||
require.NotEmpty(t, cfg1.MCPConfigFiles)
|
||||
require.Contains(t, cfg1.MCPConfigFiles[0], dir1)
|
||||
mcpFiles1 := a.contextConfigAPI.MCPConfigFiles()
|
||||
require.NotEmpty(t, mcpFiles1)
|
||||
require.Contains(t, mcpFiles1[0], dir1)
|
||||
|
||||
// Simulate manifest update on reconnection — no field
|
||||
// Simulate manifest update on reconnection -- no field
|
||||
// reassignment needed, the lazy closure picks it up.
|
||||
a.manifest.Store(&agentsdk.Manifest{Directory: dir2})
|
||||
cfg2 := a.contextConfigAPI.Config()
|
||||
require.NotEmpty(t, cfg2.MCPConfigFiles)
|
||||
require.Contains(t, cfg2.MCPConfigFiles[0], dir2)
|
||||
mcpFiles2 := a.contextConfigAPI.MCPConfigFiles()
|
||||
require.NotEmpty(t, mcpFiles2)
|
||||
require.Contains(t, mcpFiles2[0], dir2)
|
||||
}
|
||||
|
||||
@@ -986,6 +986,161 @@ func TestAgent_TCPRemoteForwarding(t *testing.T) {
|
||||
requireEcho(t, conn)
|
||||
}
|
||||
|
||||
func TestAgent_TCPLocalForwardingBlocked(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
|
||||
rl, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
defer rl.Close()
|
||||
tcpAddr, valid := rl.Addr().(*net.TCPAddr)
|
||||
require.True(t, valid)
|
||||
remotePort := tcpAddr.Port
|
||||
|
||||
//nolint:dogsled
|
||||
agentConn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0, func(_ *agenttest.Client, o *agent.Options) {
|
||||
o.BlockLocalPortForwarding = true
|
||||
})
|
||||
sshClient, err := agentConn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
defer sshClient.Close()
|
||||
|
||||
_, err = sshClient.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", remotePort))
|
||||
require.ErrorContains(t, err, "administratively prohibited")
|
||||
}
|
||||
|
||||
func TestAgent_TCPRemoteForwardingBlocked(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
|
||||
//nolint:dogsled
|
||||
agentConn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0, func(_ *agenttest.Client, o *agent.Options) {
|
||||
o.BlockReversePortForwarding = true
|
||||
})
|
||||
sshClient, err := agentConn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
defer sshClient.Close()
|
||||
|
||||
localhost := netip.MustParseAddr("127.0.0.1")
|
||||
randomPort := testutil.RandomPortNoListen(t)
|
||||
addr := net.TCPAddrFromAddrPort(netip.AddrPortFrom(localhost, randomPort))
|
||||
_, err = sshClient.ListenTCP(addr)
|
||||
require.ErrorContains(t, err, "tcpip-forward request denied by peer")
|
||||
}
|
||||
|
||||
func TestAgent_UnixLocalForwardingBlocked(t *testing.T) {
|
||||
t.Parallel()
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("unix domain sockets are not fully supported on Windows")
|
||||
}
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
tmpdir := testutil.TempDirUnixSocket(t)
|
||||
remoteSocketPath := filepath.Join(tmpdir, "remote-socket")
|
||||
|
||||
l, err := net.Listen("unix", remoteSocketPath)
|
||||
require.NoError(t, err)
|
||||
defer l.Close()
|
||||
|
||||
//nolint:dogsled
|
||||
agentConn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0, func(_ *agenttest.Client, o *agent.Options) {
|
||||
o.BlockLocalPortForwarding = true
|
||||
})
|
||||
sshClient, err := agentConn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
defer sshClient.Close()
|
||||
|
||||
_, err = sshClient.Dial("unix", remoteSocketPath)
|
||||
require.ErrorContains(t, err, "administratively prohibited")
|
||||
}
|
||||
|
||||
func TestAgent_UnixRemoteForwardingBlocked(t *testing.T) {
|
||||
t.Parallel()
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("unix domain sockets are not fully supported on Windows")
|
||||
}
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
tmpdir := testutil.TempDirUnixSocket(t)
|
||||
remoteSocketPath := filepath.Join(tmpdir, "remote-socket")
|
||||
|
||||
//nolint:dogsled
|
||||
agentConn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0, func(_ *agenttest.Client, o *agent.Options) {
|
||||
o.BlockReversePortForwarding = true
|
||||
})
|
||||
sshClient, err := agentConn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
defer sshClient.Close()
|
||||
|
||||
_, err = sshClient.ListenUnix(remoteSocketPath)
|
||||
require.ErrorContains(t, err, "streamlocal-forward@openssh.com request denied by peer")
|
||||
}
|
||||
|
||||
// TestAgent_LocalBlockedDoesNotAffectReverse verifies that blocking
|
||||
// local port forwarding does not prevent reverse port forwarding from
|
||||
// working. A field-name transposition at any plumbing hop would cause
|
||||
// both directions to be blocked when only one flag is set.
|
||||
func TestAgent_LocalBlockedDoesNotAffectReverse(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
|
||||
//nolint:dogsled
|
||||
agentConn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0, func(_ *agenttest.Client, o *agent.Options) {
|
||||
o.BlockLocalPortForwarding = true
|
||||
})
|
||||
sshClient, err := agentConn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
defer sshClient.Close()
|
||||
|
||||
// Reverse forwarding must still work.
|
||||
localhost := netip.MustParseAddr("127.0.0.1")
|
||||
var ll net.Listener
|
||||
for {
|
||||
randomPort := testutil.RandomPortNoListen(t)
|
||||
addr := net.TCPAddrFromAddrPort(netip.AddrPortFrom(localhost, randomPort))
|
||||
ll, err = sshClient.ListenTCP(addr)
|
||||
if err != nil {
|
||||
t.Logf("error remote forwarding: %s", err.Error())
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Fatal("timed out getting random listener")
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
_ = ll.Close()
|
||||
}
|
||||
|
||||
// TestAgent_ReverseBlockedDoesNotAffectLocal verifies that blocking
|
||||
// reverse port forwarding does not prevent local port forwarding from
|
||||
// working.
|
||||
func TestAgent_ReverseBlockedDoesNotAffectLocal(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
|
||||
rl, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
defer rl.Close()
|
||||
tcpAddr, valid := rl.Addr().(*net.TCPAddr)
|
||||
require.True(t, valid)
|
||||
remotePort := tcpAddr.Port
|
||||
go echoOnce(t, rl)
|
||||
|
||||
//nolint:dogsled
|
||||
agentConn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0, func(_ *agenttest.Client, o *agent.Options) {
|
||||
o.BlockReversePortForwarding = true
|
||||
})
|
||||
sshClient, err := agentConn.SSHClient(ctx)
|
||||
require.NoError(t, err)
|
||||
defer sshClient.Close()
|
||||
|
||||
// Local forwarding must still work.
|
||||
conn, err := sshClient.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", remotePort))
|
||||
require.NoError(t, err)
|
||||
defer conn.Close()
|
||||
requireEcho(t, conn)
|
||||
}
|
||||
|
||||
func TestAgent_UnixLocalForwarding(t *testing.T) {
|
||||
t.Parallel()
|
||||
if runtime.GOOS == "windows" {
|
||||
|
||||
+251
-22
@@ -2,13 +2,17 @@ package agentcontextconfig
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/codersdk/workspacesdk"
|
||||
)
|
||||
|
||||
@@ -22,9 +26,47 @@ const (
|
||||
EnvMCPConfigFiles = "CODER_AGENT_EXP_MCP_CONFIG_FILES"
|
||||
)
|
||||
|
||||
// Defaults are defined in codersdk/workspacesdk so both
|
||||
// the agent and server can reference them without a
|
||||
// cross-layer import.
|
||||
const (
|
||||
maxInstructionFileBytes = 64 * 1024
|
||||
maxSkillMetaBytes = 64 * 1024
|
||||
)
|
||||
|
||||
// markdownCommentPattern strips HTML comments from instruction
|
||||
// file content for security (prevents hidden prompt injection).
|
||||
var markdownCommentPattern = regexp.MustCompile(`<!--[\s\S]*?-->`)
|
||||
|
||||
// invisibleRunePattern strips invisible Unicode characters that
|
||||
// could be used for prompt injection.
|
||||
//
|
||||
//nolint:gocritic // Non-ASCII char ranges are intentional for invisible Unicode stripping.
|
||||
var invisibleRunePattern = regexp.MustCompile(
|
||||
"[\u00ad\u034f\u061c\u070f" +
|
||||
"\u115f\u1160\u17b4\u17b5" +
|
||||
"\u180b-\u180f" +
|
||||
"\u200b\u200d\u200e\u200f" +
|
||||
"\u202a-\u202e" +
|
||||
"\u2060-\u206f" +
|
||||
"\u3164" +
|
||||
"\ufe00-\ufe0f" +
|
||||
"\ufeff" +
|
||||
"\uffa0" +
|
||||
"\ufff0-\ufff8]",
|
||||
)
|
||||
|
||||
// skillNamePattern validates kebab-case skill names.
|
||||
var skillNamePattern = regexp.MustCompile(
|
||||
`^[a-z0-9]+(-[a-z0-9]+)*$`,
|
||||
)
|
||||
|
||||
// Default values for agent-internal configuration. These are
|
||||
// used when the corresponding env vars are unset.
|
||||
const (
|
||||
DefaultInstructionsDir = "~/.coder"
|
||||
DefaultInstructionsFile = "AGENTS.md"
|
||||
DefaultSkillsDir = ".agents/skills"
|
||||
DefaultSkillMetaFile = "SKILL.md"
|
||||
DefaultMCPConfigFile = ".mcp.json"
|
||||
)
|
||||
|
||||
// API exposes the resolved context configuration through the
|
||||
// agent's HTTP API.
|
||||
@@ -42,33 +84,61 @@ func NewAPI(workingDir func() string) *API {
|
||||
return &API{workingDir: workingDir}
|
||||
}
|
||||
|
||||
// Config reads env vars and resolves paths. Exported for use
|
||||
// by the MCP manager and tests.
|
||||
func Config(workingDir string) workspacesdk.ContextConfigResponse {
|
||||
// Config reads env vars, resolves paths, reads instruction files,
|
||||
// and discovers skills. Returns the HTTP response and the resolved
|
||||
// MCP config file paths (used only agent-internally). Exported
|
||||
// for use by tests.
|
||||
func Config(workingDir string) (workspacesdk.ContextConfigResponse, []string) {
|
||||
// TrimSpace all env vars before cmp.Or so that a
|
||||
// whitespace-only value falls through to the default
|
||||
// consistently. ResolvePaths also trims each comma-
|
||||
// separated entry, but without pre-trimming here a
|
||||
// bare " " would bypass cmp.Or and produce nil.
|
||||
instructionsDir := cmp.Or(strings.TrimSpace(os.Getenv(EnvInstructionsDirs)), workspacesdk.DefaultInstructionsDir)
|
||||
instructionsFile := cmp.Or(strings.TrimSpace(os.Getenv(EnvInstructionsFile)), workspacesdk.DefaultInstructionsFile)
|
||||
skillsDir := cmp.Or(strings.TrimSpace(os.Getenv(EnvSkillsDirs)), workspacesdk.DefaultSkillsDir)
|
||||
skillMetaFile := cmp.Or(strings.TrimSpace(os.Getenv(EnvSkillMetaFile)), workspacesdk.DefaultSkillMetaFile)
|
||||
mcpConfigFile := cmp.Or(strings.TrimSpace(os.Getenv(EnvMCPConfigFiles)), workspacesdk.DefaultMCPConfigFile)
|
||||
instructionsDir := cmp.Or(strings.TrimSpace(os.Getenv(EnvInstructionsDirs)), DefaultInstructionsDir)
|
||||
instructionsFile := cmp.Or(strings.TrimSpace(os.Getenv(EnvInstructionsFile)), DefaultInstructionsFile)
|
||||
skillsDir := cmp.Or(strings.TrimSpace(os.Getenv(EnvSkillsDirs)), DefaultSkillsDir)
|
||||
skillMetaFile := cmp.Or(strings.TrimSpace(os.Getenv(EnvSkillMetaFile)), DefaultSkillMetaFile)
|
||||
mcpConfigFile := cmp.Or(strings.TrimSpace(os.Getenv(EnvMCPConfigFiles)), DefaultMCPConfigFile)
|
||||
|
||||
resolvedInstructionsDirs := ResolvePaths(instructionsDir, workingDir)
|
||||
resolvedSkillsDirs := ResolvePaths(skillsDir, workingDir)
|
||||
|
||||
// Read instruction files from each configured directory.
|
||||
parts := readInstructionFiles(resolvedInstructionsDirs, instructionsFile)
|
||||
|
||||
// Also check the working directory for the instruction file,
|
||||
// unless it was already covered by InstructionsDirs.
|
||||
if workingDir != "" {
|
||||
seenDirs := make(map[string]struct{}, len(resolvedInstructionsDirs))
|
||||
for _, d := range resolvedInstructionsDirs {
|
||||
seenDirs[d] = struct{}{}
|
||||
}
|
||||
if _, ok := seenDirs[workingDir]; !ok {
|
||||
if entry, found := readInstructionFileFromDir(workingDir, instructionsFile); found {
|
||||
parts = append(parts, entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Discover skills from each configured skills directory.
|
||||
skillParts := discoverSkills(resolvedSkillsDirs, skillMetaFile)
|
||||
parts = append(parts, skillParts...)
|
||||
|
||||
// Guarantee non-nil slice to signal agent support.
|
||||
if parts == nil {
|
||||
parts = []codersdk.ChatMessagePart{}
|
||||
}
|
||||
|
||||
return workspacesdk.ContextConfigResponse{
|
||||
InstructionsDirs: ResolvePaths(instructionsDir, workingDir),
|
||||
InstructionsFile: instructionsFile,
|
||||
SkillsDirs: ResolvePaths(skillsDir, workingDir),
|
||||
SkillMetaFile: skillMetaFile,
|
||||
MCPConfigFiles: ResolvePaths(mcpConfigFile, workingDir),
|
||||
}
|
||||
Parts: parts,
|
||||
}, ResolvePaths(mcpConfigFile, workingDir)
|
||||
}
|
||||
|
||||
// Config returns the resolved config for use by other agent
|
||||
// components (e.g. MCP manager).
|
||||
func (api *API) Config() workspacesdk.ContextConfigResponse {
|
||||
return Config(api.workingDir())
|
||||
// MCPConfigFiles returns the resolved MCP configuration file
|
||||
// paths for the agent's MCP manager.
|
||||
func (api *API) MCPConfigFiles() []string {
|
||||
_, mcpFiles := Config(api.workingDir())
|
||||
return mcpFiles
|
||||
}
|
||||
|
||||
// Routes returns the HTTP handler for the context config
|
||||
@@ -80,5 +150,164 @@ func (api *API) Routes() http.Handler {
|
||||
}
|
||||
|
||||
func (api *API) handleGet(rw http.ResponseWriter, r *http.Request) {
|
||||
httpapi.Write(r.Context(), rw, http.StatusOK, api.Config())
|
||||
response, _ := Config(api.workingDir())
|
||||
httpapi.Write(r.Context(), rw, http.StatusOK, response)
|
||||
}
|
||||
|
||||
// readInstructionFiles reads instruction files from each given
|
||||
// directory. Missing directories are silently skipped. Duplicate
|
||||
// directories are deduplicated.
|
||||
func readInstructionFiles(dirs []string, fileName string) []codersdk.ChatMessagePart {
|
||||
var parts []codersdk.ChatMessagePart
|
||||
seen := make(map[string]struct{}, len(dirs))
|
||||
for _, dir := range dirs {
|
||||
if _, ok := seen[dir]; ok {
|
||||
continue
|
||||
}
|
||||
seen[dir] = struct{}{}
|
||||
if part, found := readInstructionFileFromDir(dir, fileName); found {
|
||||
parts = append(parts, part)
|
||||
}
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
// readInstructionFileFromDir scans a directory for a file matching
|
||||
// fileName (case-insensitive) and reads its contents.
|
||||
func readInstructionFileFromDir(dir, fileName string) (codersdk.ChatMessagePart, bool) {
|
||||
dirEntries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return codersdk.ChatMessagePart{}, false
|
||||
}
|
||||
|
||||
for _, e := range dirEntries {
|
||||
if e.IsDir() {
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(strings.TrimSpace(e.Name()), fileName) {
|
||||
filePath := filepath.Join(dir, e.Name())
|
||||
content, truncated, ok := readAndSanitizeFile(filePath, maxInstructionFileBytes)
|
||||
if !ok {
|
||||
return codersdk.ChatMessagePart{}, false
|
||||
}
|
||||
if content == "" {
|
||||
return codersdk.ChatMessagePart{}, false
|
||||
}
|
||||
return codersdk.ChatMessagePart{
|
||||
Type: codersdk.ChatMessagePartTypeContextFile,
|
||||
ContextFilePath: filePath,
|
||||
ContextFileContent: content,
|
||||
ContextFileTruncated: truncated,
|
||||
}, true
|
||||
}
|
||||
}
|
||||
return codersdk.ChatMessagePart{}, false
|
||||
}
|
||||
|
||||
// readAndSanitizeFile reads the file at path, capping the read
|
||||
// at maxBytes to avoid unbounded memory allocation. It sanitizes
|
||||
// the content (strips HTML comments and invisible Unicode) and
|
||||
// returns the result. Returns false if the file cannot be read.
|
||||
func readAndSanitizeFile(path string, maxBytes int64) (content string, truncated bool, ok bool) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", false, false
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Read at most maxBytes+1 to detect truncation without
|
||||
// allocating the entire file into memory.
|
||||
raw, err := io.ReadAll(io.LimitReader(f, maxBytes+1))
|
||||
if err != nil {
|
||||
return "", false, false
|
||||
}
|
||||
|
||||
truncated = int64(len(raw)) > maxBytes
|
||||
if truncated {
|
||||
raw = raw[:maxBytes]
|
||||
}
|
||||
|
||||
s := sanitizeInstructionMarkdown(string(raw))
|
||||
if s == "" {
|
||||
return "", truncated, true
|
||||
}
|
||||
return s, truncated, true
|
||||
}
|
||||
|
||||
// sanitizeInstructionMarkdown strips HTML comments, invisible
|
||||
// Unicode characters, and CRLF line endings from instruction
|
||||
// file content.
|
||||
func sanitizeInstructionMarkdown(content string) string {
|
||||
content = strings.ReplaceAll(content, "\r\n", "\n")
|
||||
content = strings.ReplaceAll(content, "\r", "\n")
|
||||
content = markdownCommentPattern.ReplaceAllString(content, "")
|
||||
content = invisibleRunePattern.ReplaceAllString(content, "")
|
||||
return strings.TrimSpace(content)
|
||||
}
|
||||
|
||||
// discoverSkills walks the given skills directories and returns
|
||||
// metadata for every valid skill it finds. Body and supporting
|
||||
// file lists are NOT included; chatd fetches those on demand
|
||||
// via read_skill. Missing directories or individual errors are
|
||||
// silently skipped.
|
||||
func discoverSkills(skillsDirs []string, metaFile string) []codersdk.ChatMessagePart {
|
||||
seen := make(map[string]struct{})
|
||||
var parts []codersdk.ChatMessagePart
|
||||
|
||||
for _, skillsDir := range skillsDirs {
|
||||
entries, err := os.ReadDir(skillsDir)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
metaPath := filepath.Join(skillsDir, entry.Name(), metaFile)
|
||||
f, err := os.Open(metaPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
raw, err := io.ReadAll(io.LimitReader(f, maxSkillMetaBytes+1))
|
||||
_ = f.Close()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if int64(len(raw)) > maxSkillMetaBytes {
|
||||
raw = raw[:maxSkillMetaBytes]
|
||||
}
|
||||
|
||||
name, description, _, err := workspacesdk.ParseSkillFrontmatter(string(raw))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// The directory name must match the declared name.
|
||||
if name != entry.Name() {
|
||||
continue
|
||||
}
|
||||
if !skillNamePattern.MatchString(name) {
|
||||
continue
|
||||
}
|
||||
|
||||
// First occurrence wins across directories.
|
||||
if _, ok := seen[name]; ok {
|
||||
continue
|
||||
}
|
||||
seen[name] = struct{}{}
|
||||
|
||||
skillDir := filepath.Join(skillsDir, entry.Name())
|
||||
parts = append(parts, codersdk.ChatMessagePart{
|
||||
Type: codersdk.ChatMessagePartTypeSkill,
|
||||
SkillName: name,
|
||||
SkillDescription: description,
|
||||
SkillDir: skillDir,
|
||||
ContextFileSkillMetaFile: metaFile,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return parts
|
||||
}
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
package agentcontextconfig_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/agent/agentcontextconfig"
|
||||
"github.com/coder/coder/v2/codersdk/workspacesdk"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
// filterParts returns only the parts matching the given type.
|
||||
func filterParts(parts []codersdk.ChatMessagePart, t codersdk.ChatMessagePartType) []codersdk.ChatMessagePart {
|
||||
var out []codersdk.ChatMessagePart
|
||||
for _, p := range parts {
|
||||
if p.Type == t {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
t.Run("Defaults", func(t *testing.T) {
|
||||
fakeHome := t.TempDir()
|
||||
@@ -24,19 +37,13 @@ func TestConfig(t *testing.T) {
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := platformAbsPath("work")
|
||||
cfg := agentcontextconfig.Config(workDir)
|
||||
cfg, mcpFiles := agentcontextconfig.Config(workDir)
|
||||
|
||||
require.Equal(t, workspacesdk.DefaultInstructionsFile, cfg.InstructionsFile)
|
||||
require.Equal(t, workspacesdk.DefaultSkillMetaFile, cfg.SkillMetaFile)
|
||||
// Default instructions dir is "~/.coder" which resolves
|
||||
// to the home directory.
|
||||
require.Equal(t, []string{filepath.Join(fakeHome, ".coder")}, cfg.InstructionsDirs)
|
||||
// Default skills dir is ".agents/skills" (relative),
|
||||
// resolved against the working directory.
|
||||
require.Equal(t, []string{filepath.Join(workDir, ".agents", "skills")}, cfg.SkillsDirs)
|
||||
// Parts is always non-nil.
|
||||
require.NotNil(t, cfg.Parts)
|
||||
// Default MCP config file is ".mcp.json" (relative),
|
||||
// resolved against the working directory.
|
||||
require.Equal(t, []string{filepath.Join(workDir, ".mcp.json")}, cfg.MCPConfigFiles)
|
||||
require.Equal(t, []string{filepath.Join(workDir, ".mcp.json")}, mcpFiles)
|
||||
})
|
||||
|
||||
t.Run("CustomEnvVars", func(t *testing.T) {
|
||||
@@ -44,8 +51,8 @@ func TestConfig(t *testing.T) {
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
|
||||
optInstructions := platformAbsPath("opt", "instructions")
|
||||
optSkills := platformAbsPath("opt", "skills")
|
||||
optInstructions := t.TempDir()
|
||||
optSkills := t.TempDir()
|
||||
optMCP := platformAbsPath("opt", "mcp.json")
|
||||
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, optInstructions)
|
||||
@@ -54,32 +61,58 @@ func TestConfig(t *testing.T) {
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "META.yaml")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, optMCP)
|
||||
|
||||
workDir := platformAbsPath("work")
|
||||
cfg := agentcontextconfig.Config(workDir)
|
||||
// Create files matching the custom names so we can
|
||||
// verify the env vars actually change lookup behavior.
|
||||
require.NoError(t, os.WriteFile(filepath.Join(optInstructions, "CUSTOM.md"), []byte("custom instructions"), 0o600))
|
||||
skillDir := filepath.Join(optSkills, "my-skill")
|
||||
require.NoError(t, os.MkdirAll(skillDir, 0o755))
|
||||
require.NoError(t, os.WriteFile(
|
||||
filepath.Join(skillDir, "META.yaml"),
|
||||
[]byte("---\nname: my-skill\ndescription: custom meta\n---\n"),
|
||||
0o600,
|
||||
))
|
||||
|
||||
require.Equal(t, "CUSTOM.md", cfg.InstructionsFile)
|
||||
require.Equal(t, "META.yaml", cfg.SkillMetaFile)
|
||||
require.Equal(t, []string{optInstructions}, cfg.InstructionsDirs)
|
||||
require.Equal(t, []string{optSkills}, cfg.SkillsDirs)
|
||||
require.Equal(t, []string{optMCP}, cfg.MCPConfigFiles)
|
||||
workDir := platformAbsPath("work")
|
||||
cfg, mcpFiles := agentcontextconfig.Config(workDir)
|
||||
|
||||
require.Equal(t, []string{optMCP}, mcpFiles)
|
||||
ctxFiles := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeContextFile)
|
||||
require.Len(t, ctxFiles, 1)
|
||||
require.Equal(t, "custom instructions", ctxFiles[0].ContextFileContent)
|
||||
skillParts := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeSkill)
|
||||
require.Len(t, skillParts, 1)
|
||||
require.Equal(t, "my-skill", skillParts[0].SkillName)
|
||||
require.Equal(t, "META.yaml", skillParts[0].ContextFileSkillMetaFile)
|
||||
})
|
||||
|
||||
t.Run("WhitespaceInFileNames", func(t *testing.T) {
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, "")
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, " CLAUDE.md ")
|
||||
t.Setenv(agentcontextconfig.EnvSkillsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := platformAbsPath("work")
|
||||
cfg := agentcontextconfig.Config(workDir)
|
||||
workDir := t.TempDir()
|
||||
// Create a file matching the trimmed name.
|
||||
require.NoError(t, os.WriteFile(filepath.Join(fakeHome, "CLAUDE.md"), []byte("hello"), 0o600))
|
||||
|
||||
require.Equal(t, "CLAUDE.md", cfg.InstructionsFile)
|
||||
cfg, _ := agentcontextconfig.Config(workDir)
|
||||
|
||||
ctxFiles := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeContextFile)
|
||||
require.Len(t, ctxFiles, 1)
|
||||
require.Equal(t, "hello", ctxFiles[0].ContextFileContent)
|
||||
})
|
||||
|
||||
t.Run("CommaSeparatedDirs", func(t *testing.T) {
|
||||
a := platformAbsPath("opt", "a")
|
||||
b := platformAbsPath("opt", "b")
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
|
||||
a := t.TempDir()
|
||||
b := t.TempDir()
|
||||
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, a+","+b)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
||||
@@ -87,10 +120,300 @@ func TestConfig(t *testing.T) {
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := platformAbsPath("work")
|
||||
cfg := agentcontextconfig.Config(workDir)
|
||||
// Put instruction files in both dirs.
|
||||
require.NoError(t, os.WriteFile(filepath.Join(a, "AGENTS.md"), []byte("from a"), 0o600))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(b, "AGENTS.md"), []byte("from b"), 0o600))
|
||||
|
||||
require.Equal(t, []string{a, b}, cfg.InstructionsDirs)
|
||||
workDir := t.TempDir()
|
||||
cfg, _ := agentcontextconfig.Config(workDir)
|
||||
|
||||
ctxFiles := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeContextFile)
|
||||
require.Len(t, ctxFiles, 2)
|
||||
require.Equal(t, "from a", ctxFiles[0].ContextFileContent)
|
||||
require.Equal(t, "from b", ctxFiles[1].ContextFileContent)
|
||||
})
|
||||
|
||||
t.Run("ReadsInstructionFiles", func(t *testing.T) {
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := t.TempDir()
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
|
||||
// Create ~/.coder/AGENTS.md
|
||||
coderDir := filepath.Join(fakeHome, ".coder")
|
||||
require.NoError(t, os.MkdirAll(coderDir, 0o755))
|
||||
require.NoError(t, os.WriteFile(
|
||||
filepath.Join(coderDir, "AGENTS.md"),
|
||||
[]byte("home instructions"),
|
||||
0o600,
|
||||
))
|
||||
|
||||
cfg, _ := agentcontextconfig.Config(workDir)
|
||||
|
||||
ctxFiles := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeContextFile)
|
||||
require.NotNil(t, cfg.Parts)
|
||||
require.Len(t, ctxFiles, 1)
|
||||
require.Equal(t, "home instructions", ctxFiles[0].ContextFileContent)
|
||||
require.Equal(t, filepath.Join(coderDir, "AGENTS.md"), ctxFiles[0].ContextFilePath)
|
||||
require.False(t, ctxFiles[0].ContextFileTruncated)
|
||||
})
|
||||
|
||||
t.Run("ReadsWorkingDirInstructionFile", func(t *testing.T) {
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := t.TempDir()
|
||||
|
||||
// Create AGENTS.md in the working directory.
|
||||
require.NoError(t, os.WriteFile(
|
||||
filepath.Join(workDir, "AGENTS.md"),
|
||||
[]byte("project instructions"),
|
||||
0o600,
|
||||
))
|
||||
|
||||
cfg, _ := agentcontextconfig.Config(workDir)
|
||||
|
||||
// Should find the working dir file (not in instruction dirs).
|
||||
ctxFiles := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeContextFile)
|
||||
require.NotNil(t, cfg.Parts)
|
||||
require.Len(t, ctxFiles, 1)
|
||||
require.Equal(t, "project instructions", ctxFiles[0].ContextFileContent)
|
||||
require.Equal(t, filepath.Join(workDir, "AGENTS.md"), ctxFiles[0].ContextFilePath)
|
||||
})
|
||||
|
||||
t.Run("TruncatesLargeInstructionFile", func(t *testing.T) {
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := t.TempDir()
|
||||
largeContent := strings.Repeat("a", 64*1024+100)
|
||||
require.NoError(t, os.WriteFile(filepath.Join(workDir, "AGENTS.md"), []byte(largeContent), 0o600))
|
||||
|
||||
cfg, _ := agentcontextconfig.Config(workDir)
|
||||
|
||||
ctxFiles := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeContextFile)
|
||||
require.Len(t, ctxFiles, 1)
|
||||
require.True(t, ctxFiles[0].ContextFileTruncated)
|
||||
require.Len(t, ctxFiles[0].ContextFileContent, 64*1024)
|
||||
})
|
||||
|
||||
t.Run("SanitizesHTMLComments", func(t *testing.T) {
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(
|
||||
filepath.Join(workDir, "AGENTS.md"),
|
||||
[]byte("visible\n<!-- hidden -->content"),
|
||||
0o600,
|
||||
))
|
||||
|
||||
cfg, _ := agentcontextconfig.Config(workDir)
|
||||
|
||||
ctxFiles := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeContextFile)
|
||||
require.Len(t, ctxFiles, 1)
|
||||
require.Equal(t, "visible\ncontent", ctxFiles[0].ContextFileContent)
|
||||
})
|
||||
|
||||
t.Run("SanitizesInvisibleUnicode", func(t *testing.T) {
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := t.TempDir()
|
||||
// U+200B (zero-width space) should be stripped.
|
||||
require.NoError(t, os.WriteFile(
|
||||
filepath.Join(workDir, "AGENTS.md"),
|
||||
[]byte("before\u200bafter"),
|
||||
0o600,
|
||||
))
|
||||
|
||||
cfg, _ := agentcontextconfig.Config(workDir)
|
||||
|
||||
ctxFiles := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeContextFile)
|
||||
require.Len(t, ctxFiles, 1)
|
||||
require.Equal(t, "beforeafter", ctxFiles[0].ContextFileContent)
|
||||
})
|
||||
|
||||
t.Run("NormalizesCRLF", func(t *testing.T) {
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(
|
||||
filepath.Join(workDir, "AGENTS.md"),
|
||||
[]byte("line1\r\nline2\rline3"),
|
||||
0o600,
|
||||
))
|
||||
|
||||
cfg, _ := agentcontextconfig.Config(workDir)
|
||||
|
||||
ctxFiles := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeContextFile)
|
||||
require.Len(t, ctxFiles, 1)
|
||||
require.Equal(t, "line1\nline2\nline3", ctxFiles[0].ContextFileContent)
|
||||
})
|
||||
|
||||
t.Run("DiscoversSkills", func(t *testing.T) {
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := t.TempDir()
|
||||
skillsDir := filepath.Join(workDir, ".agents", "skills")
|
||||
t.Setenv(agentcontextconfig.EnvSkillsDirs, skillsDir)
|
||||
|
||||
// Create a valid skill.
|
||||
skillDir := filepath.Join(skillsDir, "my-skill")
|
||||
require.NoError(t, os.MkdirAll(skillDir, 0o755))
|
||||
require.NoError(t, os.WriteFile(
|
||||
filepath.Join(skillDir, "SKILL.md"),
|
||||
[]byte("---\nname: my-skill\ndescription: A test skill\n---\nSkill body"),
|
||||
0o600,
|
||||
))
|
||||
|
||||
cfg, _ := agentcontextconfig.Config(workDir)
|
||||
|
||||
skillParts := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeSkill)
|
||||
require.Len(t, skillParts, 1)
|
||||
require.Equal(t, "my-skill", skillParts[0].SkillName)
|
||||
require.Equal(t, "A test skill", skillParts[0].SkillDescription)
|
||||
require.Equal(t, skillDir, skillParts[0].SkillDir)
|
||||
require.Equal(t, "SKILL.md", skillParts[0].ContextFileSkillMetaFile)
|
||||
})
|
||||
|
||||
t.Run("SkipsMissingDirs", func(t *testing.T) {
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
|
||||
nonExistent := filepath.Join(t.TempDir(), "does-not-exist")
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, nonExistent)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillsDirs, nonExistent)
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := t.TempDir()
|
||||
cfg, _ := agentcontextconfig.Config(workDir)
|
||||
|
||||
// Non-nil empty slice (signals agent supports new format).
|
||||
require.NotNil(t, cfg.Parts)
|
||||
require.Empty(t, cfg.Parts)
|
||||
})
|
||||
|
||||
t.Run("MCPConfigFilesResolvedSeparately", func(t *testing.T) {
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillsDirs, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
|
||||
optMCP := platformAbsPath("opt", "custom.json")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, optMCP)
|
||||
|
||||
workDir := t.TempDir()
|
||||
_, mcpFiles := agentcontextconfig.Config(workDir)
|
||||
|
||||
require.Equal(t, []string{optMCP}, mcpFiles)
|
||||
})
|
||||
|
||||
t.Run("SkillNameMustMatchDir", func(t *testing.T) {
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := t.TempDir()
|
||||
skillsDir := filepath.Join(workDir, "skills")
|
||||
t.Setenv(agentcontextconfig.EnvSkillsDirs, skillsDir)
|
||||
|
||||
// Skill name in frontmatter doesn't match directory name.
|
||||
skillDir := filepath.Join(skillsDir, "wrong-dir-name")
|
||||
require.NoError(t, os.MkdirAll(skillDir, 0o755))
|
||||
require.NoError(t, os.WriteFile(
|
||||
filepath.Join(skillDir, "SKILL.md"),
|
||||
[]byte("---\nname: actual-name\ndescription: mismatch\n---\n"),
|
||||
0o600,
|
||||
))
|
||||
|
||||
cfg, _ := agentcontextconfig.Config(workDir)
|
||||
skillParts := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeSkill)
|
||||
require.Empty(t, skillParts)
|
||||
})
|
||||
|
||||
t.Run("DuplicateSkillsFirstWins", func(t *testing.T) {
|
||||
fakeHome := t.TempDir()
|
||||
t.Setenv("HOME", fakeHome)
|
||||
t.Setenv("USERPROFILE", fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsDirs, fakeHome)
|
||||
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
||||
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
||||
|
||||
workDir := t.TempDir()
|
||||
skillsDir1 := filepath.Join(workDir, "skills1")
|
||||
skillsDir2 := filepath.Join(workDir, "skills2")
|
||||
t.Setenv(agentcontextconfig.EnvSkillsDirs, skillsDir1+","+skillsDir2)
|
||||
|
||||
// Same skill name in both directories.
|
||||
for _, dir := range []string{skillsDir1, skillsDir2} {
|
||||
skillDir := filepath.Join(dir, "dup-skill")
|
||||
require.NoError(t, os.MkdirAll(skillDir, 0o755))
|
||||
require.NoError(t, os.WriteFile(
|
||||
filepath.Join(skillDir, "SKILL.md"),
|
||||
[]byte("---\nname: dup-skill\ndescription: from "+filepath.Base(dir)+"\n---\n"),
|
||||
0o600,
|
||||
))
|
||||
}
|
||||
|
||||
cfg, _ := agentcontextconfig.Config(workDir)
|
||||
skillParts := filterParts(cfg.Parts, codersdk.ChatMessagePartTypeSkill)
|
||||
require.Len(t, skillParts, 1)
|
||||
require.Equal(t, "from skills1", skillParts[0].SkillDescription)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -104,14 +427,13 @@ func TestNewAPI_LazyDirectory(t *testing.T) {
|
||||
dir := ""
|
||||
api := agentcontextconfig.NewAPI(func() string { return dir })
|
||||
|
||||
// Before directory is set, relative paths resolve to nothing.
|
||||
cfg := api.Config()
|
||||
require.Empty(t, cfg.SkillsDirs)
|
||||
require.Empty(t, cfg.MCPConfigFiles)
|
||||
// Before directory is set, MCP paths resolve to nothing.
|
||||
mcpFiles := api.MCPConfigFiles()
|
||||
require.Empty(t, mcpFiles)
|
||||
|
||||
// After setting the directory, Config() picks it up lazily.
|
||||
// After setting the directory, MCPConfigFiles() picks it up.
|
||||
dir = platformAbsPath("work")
|
||||
cfg = api.Config()
|
||||
require.NotEmpty(t, cfg.SkillsDirs)
|
||||
require.Equal(t, []string{filepath.Join(dir, ".agents", "skills")}, cfg.SkillsDirs)
|
||||
mcpFiles = api.MCPConfigFiles()
|
||||
require.NotEmpty(t, mcpFiles)
|
||||
require.Equal(t, []string{filepath.Join(dir, ".mcp.json")}, mcpFiles)
|
||||
}
|
||||
|
||||
@@ -117,6 +117,10 @@ type Config struct {
|
||||
X11MaxPort *int
|
||||
// BlockFileTransfer restricts use of file transfer applications.
|
||||
BlockFileTransfer bool
|
||||
// BlockReversePortForwarding disables reverse port forwarding (ssh -R).
|
||||
BlockReversePortForwarding bool
|
||||
// BlockLocalPortForwarding disables local port forwarding (ssh -L).
|
||||
BlockLocalPortForwarding bool
|
||||
// ReportConnection.
|
||||
ReportConnection reportConnectionFunc
|
||||
// Experimental: allow connecting to running containers via Docker exec.
|
||||
@@ -190,7 +194,7 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
|
||||
}
|
||||
|
||||
forwardHandler := &ssh.ForwardedTCPHandler{}
|
||||
unixForwardHandler := newForwardedUnixHandler(logger)
|
||||
unixForwardHandler := newForwardedUnixHandler(logger, config.BlockReversePortForwarding)
|
||||
|
||||
metrics := newSSHServerMetrics(prometheusRegistry)
|
||||
s := &Server{
|
||||
@@ -229,8 +233,15 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
|
||||
wrapped := NewJetbrainsChannelWatcher(ctx, s.logger, s.config.ReportConnection, newChan, &s.connCountJetBrains)
|
||||
ssh.DirectTCPIPHandler(srv, conn, wrapped, ctx)
|
||||
},
|
||||
"direct-streamlocal@openssh.com": directStreamLocalHandler,
|
||||
"session": ssh.DefaultSessionHandler,
|
||||
"direct-streamlocal@openssh.com": func(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context) {
|
||||
if s.config.BlockLocalPortForwarding {
|
||||
s.logger.Warn(ctx, "unix local port forward blocked")
|
||||
_ = newChan.Reject(gossh.Prohibited, "local port forwarding is disabled")
|
||||
return
|
||||
}
|
||||
directStreamLocalHandler(srv, conn, newChan, ctx)
|
||||
},
|
||||
"session": ssh.DefaultSessionHandler,
|
||||
},
|
||||
ConnectionFailedCallback: func(conn net.Conn, err error) {
|
||||
s.logger.Warn(ctx, "ssh connection failed",
|
||||
@@ -250,6 +261,12 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
|
||||
// be set before we start listening.
|
||||
HostSigners: []ssh.Signer{},
|
||||
LocalPortForwardingCallback: func(ctx ssh.Context, destinationHost string, destinationPort uint32) bool {
|
||||
if s.config.BlockLocalPortForwarding {
|
||||
s.logger.Warn(ctx, "local port forward blocked",
|
||||
slog.F("destination_host", destinationHost),
|
||||
slog.F("destination_port", destinationPort))
|
||||
return false
|
||||
}
|
||||
// Allow local port forwarding all!
|
||||
s.logger.Debug(ctx, "local port forward",
|
||||
slog.F("destination_host", destinationHost),
|
||||
@@ -260,6 +277,12 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom
|
||||
return true
|
||||
},
|
||||
ReversePortForwardingCallback: func(ctx ssh.Context, bindHost string, bindPort uint32) bool {
|
||||
if s.config.BlockReversePortForwarding {
|
||||
s.logger.Warn(ctx, "reverse port forward blocked",
|
||||
slog.F("bind_host", bindHost),
|
||||
slog.F("bind_port", bindPort))
|
||||
return false
|
||||
}
|
||||
// Allow reverse port forwarding all!
|
||||
s.logger.Debug(ctx, "reverse port forward",
|
||||
slog.F("bind_host", bindHost),
|
||||
|
||||
@@ -35,8 +35,9 @@ type forwardedStreamLocalPayload struct {
|
||||
// streamlocal forwarding (aka. unix forwarding) instead of TCP forwarding.
|
||||
type forwardedUnixHandler struct {
|
||||
sync.Mutex
|
||||
log slog.Logger
|
||||
forwards map[forwardKey]net.Listener
|
||||
log slog.Logger
|
||||
forwards map[forwardKey]net.Listener
|
||||
blockReversePortForwarding bool
|
||||
}
|
||||
|
||||
type forwardKey struct {
|
||||
@@ -44,10 +45,11 @@ type forwardKey struct {
|
||||
addr string
|
||||
}
|
||||
|
||||
func newForwardedUnixHandler(log slog.Logger) *forwardedUnixHandler {
|
||||
func newForwardedUnixHandler(log slog.Logger, blockReversePortForwarding bool) *forwardedUnixHandler {
|
||||
return &forwardedUnixHandler{
|
||||
log: log,
|
||||
forwards: make(map[forwardKey]net.Listener),
|
||||
log: log,
|
||||
forwards: make(map[forwardKey]net.Listener),
|
||||
blockReversePortForwarding: blockReversePortForwarding,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +64,10 @@ func (h *forwardedUnixHandler) HandleSSHRequest(ctx ssh.Context, _ *ssh.Server,
|
||||
|
||||
switch req.Type {
|
||||
case "streamlocal-forward@openssh.com":
|
||||
if h.blockReversePortForwarding {
|
||||
log.Warn(ctx, "unix reverse port forward blocked")
|
||||
return false, nil
|
||||
}
|
||||
var reqPayload streamLocalForwardPayload
|
||||
err := gossh.Unmarshal(req.Payload, &reqPayload)
|
||||
if err != nil {
|
||||
|
||||
@@ -187,7 +187,11 @@ func (*Manager) connectServer(ctx context.Context, cfg ServerConfig) (*client.Cl
|
||||
connectCtx, cancel := context.WithTimeout(ctx, connectTimeout)
|
||||
defer cancel()
|
||||
|
||||
if err := c.Start(connectCtx); err != nil {
|
||||
// Use the parent ctx (not connectCtx) so the subprocess outlives
|
||||
// the connect/initialize handshake. connectCtx bounds only the
|
||||
// Initialize call below. The subprocess is cleaned up when the
|
||||
// Manager is closed or ctx is canceled.
|
||||
if err := c.Start(ctx); err != nil {
|
||||
_ = c.Close()
|
||||
return nil, xerrors.Errorf("start %q: %w", cfg.Name, err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package agentmcp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
@@ -8,6 +13,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/codersdk/workspacesdk"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
func TestSplitToolName(t *testing.T) {
|
||||
@@ -193,3 +199,118 @@ func TestConvertResult(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestConnectServer_StdioProcessSurvivesConnect verifies that a stdio MCP
|
||||
// server subprocess remains alive after connectServer returns. This is a
|
||||
// regression test for a bug where the subprocess was tied to a short-lived
|
||||
// connectCtx and killed as soon as the context was canceled.
|
||||
func TestConnectServer_StdioProcessSurvivesConnect(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if os.Getenv("TEST_MCP_FAKE_SERVER") == "1" {
|
||||
// Child process: act as a minimal MCP server over stdio.
|
||||
runFakeMCPServer()
|
||||
return
|
||||
}
|
||||
|
||||
// Get the path to the test binary so we can re-exec ourselves
|
||||
// as a fake MCP server subprocess.
|
||||
testBin, err := os.Executable()
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := ServerConfig{
|
||||
Name: "fake",
|
||||
Transport: "stdio",
|
||||
Command: testBin,
|
||||
Args: []string{"-test.run=^TestConnectServer_StdioProcessSurvivesConnect$"},
|
||||
Env: map[string]string{"TEST_MCP_FAKE_SERVER": "1"},
|
||||
}
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
m := &Manager{}
|
||||
client, err := m.connectServer(ctx, cfg)
|
||||
require.NoError(t, err, "connectServer should succeed")
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
|
||||
// At this point connectServer has returned and its internal
|
||||
// connectCtx has been canceled. The subprocess must still be
|
||||
// alive. Verify by listing tools (requires a live server).
|
||||
listCtx, listCancel := context.WithTimeout(ctx, testutil.WaitShort)
|
||||
defer listCancel()
|
||||
result, err := client.ListTools(listCtx, mcp.ListToolsRequest{})
|
||||
require.NoError(t, err, "ListTools should succeed — server must be alive after connect")
|
||||
require.Len(t, result.Tools, 1)
|
||||
assert.Equal(t, "echo", result.Tools[0].Name)
|
||||
}
|
||||
|
||||
// runFakeMCPServer implements a minimal JSON-RPC / MCP server over
|
||||
// stdin/stdout, just enough for initialize + tools/list.
|
||||
func runFakeMCPServer() {
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Bytes()
|
||||
|
||||
var req struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
ID json.RawMessage `json:"id"`
|
||||
Method string `json:"method"`
|
||||
}
|
||||
if err := json.Unmarshal(line, &req); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var resp any
|
||||
switch req.Method {
|
||||
case "initialize":
|
||||
resp = map[string]any{
|
||||
"jsonrpc": "2.0",
|
||||
"id": req.ID,
|
||||
"result": map[string]any{
|
||||
"protocolVersion": "2025-03-26",
|
||||
"capabilities": map[string]any{
|
||||
"tools": map[string]any{},
|
||||
},
|
||||
"serverInfo": map[string]any{
|
||||
"name": "fake-server",
|
||||
"version": "0.0.1",
|
||||
},
|
||||
},
|
||||
}
|
||||
case "notifications/initialized":
|
||||
// No response needed for notifications.
|
||||
continue
|
||||
case "tools/list":
|
||||
resp = map[string]any{
|
||||
"jsonrpc": "2.0",
|
||||
"id": req.ID,
|
||||
"result": map[string]any{
|
||||
"tools": []map[string]any{
|
||||
{
|
||||
"name": "echo",
|
||||
"description": "echoes input",
|
||||
"inputSchema": map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
default:
|
||||
resp = map[string]any{
|
||||
"jsonrpc": "2.0",
|
||||
"id": req.ID,
|
||||
"error": map[string]any{
|
||||
"code": -32601,
|
||||
"message": "method not found",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
out, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
_, _ = fmt.Fprintf(os.Stdout, "%s\n", out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
# aibridge
|
||||
|
||||
aibridge is an HTTP gateway that sits between AI clients and upstream AI providers (Anthropic, OpenAI). It intercepts requests to record token usage, prompts, and tool invocations per user. Optionally supports centralized [MCP](https://modelcontextprotocol.io/) tool injection with allowlist/denylist filtering.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌───────────────────────────────────────────┐
|
||||
│ AI Client │ │ aibridge │
|
||||
│ (Claude Code, │────▶│ ┌─────────────────┐ ┌─────────────┐ │
|
||||
│ Cursor, etc.) │ │ │ RequestBridge │───▶│ Providers │ │
|
||||
└─────────────────┘ │ │ (http.Handler) │ │ (Anthropic │ │
|
||||
│ └─────────────────┘ │ OpenAI) │ │
|
||||
│ └──────┬──────┘ │
|
||||
│ │ │
|
||||
│ ▼ │ ┌─────────────┐
|
||||
│ ┌─────────────────┐ ┌─────────────┐ │ │ Upstream │
|
||||
│ │ Recorder │◀───│ Interceptor │─── ───▶│ API │
|
||||
│ │ (tokens, tools, │ │ (streaming/ │ │ │ (Anthropic │
|
||||
│ │ prompts) │ │ blocking) │ │ │ OpenAI) │
|
||||
│ └────────┬────────┘ └──────┬──────┘ │ └─────────────┘
|
||||
│ │ │ │
|
||||
│ ▼ ┌──────▼──────┐ │
|
||||
│ ┌ ─ ─ ─ ─ ─ ─ ─ ┐ │ MCP Proxy │ │
|
||||
│ │ Database │ │ (tools) │ │
|
||||
│ └ ─ ─ ─ ─ ─ ─ ─ ┘ └─────────────┘ │
|
||||
└───────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Components
|
||||
|
||||
- **RequestBridge**: The main `http.Handler` that routes requests to providers
|
||||
- **Provider**: Defines bridged routes (intercepted) and passthrough routes (proxied)
|
||||
- **Interceptor**: Handles request/response processing and streaming
|
||||
- **Recorder**: Interface for capturing usage data (tokens, prompts, tools)
|
||||
- **MCP Proxy** (optional): Connects to MCP servers to list tool, inject them into requests, and invoke them in an inner agentic loop
|
||||
|
||||
## Request Flow
|
||||
|
||||
1. Client sends request to `/anthropic/v1/messages` or `/openai/v1/chat/completions`
|
||||
2. **Actor extraction**: Request must have an actor in context (via `AsActor()`).
|
||||
3. **Upstream call**: Request forwarded to the AI provider
|
||||
4. **Response relay**: Response streamed/sent to client
|
||||
5. **Recording**: Token usage, prompts, and tool invocations recorded
|
||||
|
||||
**With MCP enabled**: Tools from configured MCP servers are centrally defined and injected into requests (prefixed `bmcp_`). Allowlist/denylist regex patterns control which tools are available. When the model selects an injected tool, the gateway invokes it in an inner agentic loop, and continues the conversation loop until complete.
|
||||
|
||||
Passthrough routes (`/v1/models`, `/v1/messages/count_tokens`) are reverse-proxied directly.
|
||||
|
||||
## Observability
|
||||
|
||||
### Prometheus Metrics
|
||||
|
||||
Create metrics with `NewMetrics(prometheus.Registerer)`:
|
||||
|
||||
| Metric | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `interceptions_total` | Counter | Intercepted request count |
|
||||
| `interceptions_inflight` | Gauge | Currently processing requests |
|
||||
| `interceptions_duration_seconds` | Histogram | Request duration |
|
||||
| `tokens_total` | Counter | Token usage (input/output) |
|
||||
| `prompts_total` | Counter | User prompt count |
|
||||
| `injected_tool_invocations_total` | Counter | MCP tool invocations |
|
||||
| `passthrough_total` | Counter | Non-intercepted requests |
|
||||
|
||||
### Recorder Interface
|
||||
|
||||
Implement `Recorder` to persist usage data to your database:
|
||||
|
||||
- `aibridge_interceptions` - request metadata (provider, model, initiator, timestamps)
|
||||
- `aibridge_token_usages` - input/output token counts per response
|
||||
- `aibridge_user_prompts` - user prompts
|
||||
- `aibridge_tool_usages` - tool invocations (injected and client-defined)
|
||||
|
||||
```go
|
||||
type Recorder interface {
|
||||
RecordInterception(ctx context.Context, req *InterceptionRecord) error
|
||||
RecordInterceptionEnded(ctx context.Context, req *InterceptionRecordEnded) error
|
||||
RecordTokenUsage(ctx context.Context, req *TokenUsageRecord) error
|
||||
RecordPromptUsage(ctx context.Context, req *PromptUsageRecord) error
|
||||
RecordToolUsage(ctx context.Context, req *ToolUsageRecord) error
|
||||
}
|
||||
```
|
||||
|
||||
## Supported Routes
|
||||
|
||||
| Provider | Route | Type |
|
||||
|----------|-------|------|
|
||||
| Anthropic | `/anthropic/v1/messages` | Bridged (intercepted) |
|
||||
| Anthropic | `/anthropic/v1/models` | Passthrough |
|
||||
| Anthropic | `/anthropic/v1/messages/count_tokens` | Passthrough |
|
||||
| OpenAI | `/openai/v1/chat/completions` | Bridged (intercepted) |
|
||||
| OpenAI | `/openai/v1/models` | Passthrough |
|
||||
@@ -0,0 +1,66 @@
|
||||
package aibridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"cdr.dev/slog/v3"
|
||||
"github.com/coder/coder/v2/aibridge/config"
|
||||
aibcontext "github.com/coder/coder/v2/aibridge/context"
|
||||
"github.com/coder/coder/v2/aibridge/metrics"
|
||||
"github.com/coder/coder/v2/aibridge/provider"
|
||||
"github.com/coder/coder/v2/aibridge/recorder"
|
||||
)
|
||||
|
||||
// Const + Type + function aliases for backwards compatibility.
|
||||
const (
|
||||
ProviderAnthropic = config.ProviderAnthropic
|
||||
ProviderOpenAI = config.ProviderOpenAI
|
||||
ProviderCopilot = config.ProviderCopilot
|
||||
)
|
||||
|
||||
type (
|
||||
Metrics = metrics.Metrics
|
||||
|
||||
Provider = provider.Provider
|
||||
|
||||
InterceptionRecord = recorder.InterceptionRecord
|
||||
InterceptionRecordEnded = recorder.InterceptionRecordEnded
|
||||
TokenUsageRecord = recorder.TokenUsageRecord
|
||||
PromptUsageRecord = recorder.PromptUsageRecord
|
||||
ToolUsageRecord = recorder.ToolUsageRecord
|
||||
ModelThoughtRecord = recorder.ModelThoughtRecord
|
||||
Recorder = recorder.Recorder
|
||||
Metadata = recorder.Metadata
|
||||
|
||||
AnthropicConfig = config.Anthropic
|
||||
AWSBedrockConfig = config.AWSBedrock
|
||||
OpenAIConfig = config.OpenAI
|
||||
CopilotConfig = config.Copilot
|
||||
)
|
||||
|
||||
func AsActor(ctx context.Context, actorID string, metadata recorder.Metadata) context.Context {
|
||||
return aibcontext.AsActor(ctx, actorID, metadata)
|
||||
}
|
||||
|
||||
func NewAnthropicProvider(cfg config.Anthropic, bedrockCfg *config.AWSBedrock) provider.Provider {
|
||||
return provider.NewAnthropic(cfg, bedrockCfg)
|
||||
}
|
||||
|
||||
func NewOpenAIProvider(cfg config.OpenAI) provider.Provider {
|
||||
return provider.NewOpenAI(cfg)
|
||||
}
|
||||
|
||||
func NewCopilotProvider(cfg config.Copilot) provider.Provider {
|
||||
return provider.NewCopilot(cfg)
|
||||
}
|
||||
|
||||
func NewMetrics(reg prometheus.Registerer) *metrics.Metrics {
|
||||
return metrics.NewMetrics(reg)
|
||||
}
|
||||
|
||||
func NewRecorder(logger slog.Logger, tracer trace.Tracer, clientFn func() (Recorder, error)) Recorder {
|
||||
return recorder.NewWrappedRecorder(logger, tracer, clientFn)
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
package aibridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/sony/gobreaker/v2"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"cdr.dev/slog/v3"
|
||||
"github.com/coder/coder/v2/aibridge/circuitbreaker"
|
||||
aibcontext "github.com/coder/coder/v2/aibridge/context"
|
||||
"github.com/coder/coder/v2/aibridge/mcp"
|
||||
"github.com/coder/coder/v2/aibridge/metrics"
|
||||
"github.com/coder/coder/v2/aibridge/provider"
|
||||
"github.com/coder/coder/v2/aibridge/recorder"
|
||||
"github.com/coder/coder/v2/aibridge/tracing"
|
||||
)
|
||||
|
||||
const (
|
||||
// The duration after which an async recording will be aborted.
|
||||
recordingTimeout = time.Second * 5
|
||||
)
|
||||
|
||||
// RequestBridge is an [http.Handler] which is capable of masquerading as AI providers' APIs;
|
||||
// specifically, OpenAI's & Anthropic's at present.
|
||||
// RequestBridge intercepts requests to - and responses from - these upstream services to provide
|
||||
// a centralized governance layer.
|
||||
//
|
||||
// RequestBridge has no concept of authentication or authorization. It does have a concept of identity,
|
||||
// in the narrow sense that it expects an [actor] to be defined in the context, to record the initiator
|
||||
// of each interception.
|
||||
//
|
||||
// RequestBridge is safe for concurrent use.
|
||||
type RequestBridge struct {
|
||||
mux *http.ServeMux
|
||||
logger slog.Logger
|
||||
|
||||
mcpProxy mcp.ServerProxier
|
||||
|
||||
inflightReqs atomic.Int32
|
||||
inflightWG sync.WaitGroup // For graceful shutdown.
|
||||
|
||||
inflightCtx context.Context
|
||||
inflightCancel func()
|
||||
|
||||
shutdownOnce sync.Once
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
var _ http.Handler = &RequestBridge{}
|
||||
|
||||
// validProviderName matches names containing only lowercase alphanumeric characters and hyphens.
|
||||
var validProviderName = regexp.MustCompile(`^[a-z0-9]+(-[a-z0-9]+)*$`)
|
||||
|
||||
// validateProviders checks that provider names are valid and unique.
|
||||
func validateProviders(providers []provider.Provider) error {
|
||||
names := make(map[string]bool, len(providers))
|
||||
for _, prov := range providers {
|
||||
name := prov.Name()
|
||||
if !validProviderName.MatchString(name) {
|
||||
return xerrors.Errorf("invalid provider name %q: must contain only lowercase alphanumeric characters and hyphens", name)
|
||||
}
|
||||
if names[name] {
|
||||
return xerrors.Errorf("duplicate provider name: %q", name)
|
||||
}
|
||||
names[name] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewRequestBridge creates a new *[RequestBridge] and registers the HTTP routes defined by the given providers.
|
||||
// Any routes which are requested but not registered will be reverse-proxied to the upstream service.
|
||||
//
|
||||
// A [intercept.Recorder] is also required to record prompt, tool, and token use.
|
||||
//
|
||||
// mcpProxy will be closed when the [RequestBridge] is closed.
|
||||
//
|
||||
// Circuit breaker configuration is obtained from each provider's CircuitBreakerConfig() method.
|
||||
// Providers returning nil will not have circuit breaker protection.
|
||||
func NewRequestBridge(ctx context.Context, providers []provider.Provider, rec recorder.Recorder, mcpProxy mcp.ServerProxier, logger slog.Logger, m *metrics.Metrics, tracer trace.Tracer) (*RequestBridge, error) {
|
||||
if err := validateProviders(providers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
for _, prov := range providers {
|
||||
// Create per-provider circuit breaker if configured
|
||||
cfg := prov.CircuitBreakerConfig()
|
||||
providerName := prov.Name()
|
||||
onChange := func(endpoint, model string, from, to gobreaker.State) {
|
||||
logger.Info(context.Background(), "circuit breaker state change",
|
||||
slog.F("provider", providerName),
|
||||
slog.F("endpoint", endpoint),
|
||||
slog.F("model", model),
|
||||
slog.F("from", from.String()),
|
||||
slog.F("to", to.String()),
|
||||
)
|
||||
if m != nil {
|
||||
m.CircuitBreakerState.WithLabelValues(providerName, endpoint, model).Set(circuitbreaker.StateToGaugeValue(to))
|
||||
if to == gobreaker.StateOpen {
|
||||
m.CircuitBreakerTrips.WithLabelValues(providerName, endpoint, model).Inc()
|
||||
}
|
||||
}
|
||||
}
|
||||
cbs := circuitbreaker.NewProviderCircuitBreakers(providerName, cfg, onChange, m)
|
||||
|
||||
// Add the known provider-specific routes which are bridged (i.e. intercepted and augmented).
|
||||
for _, path := range prov.BridgedRoutes() {
|
||||
handler := newInterceptionProcessor(prov, cbs, rec, mcpProxy, logger, m, tracer)
|
||||
route, err := url.JoinPath(prov.RoutePrefix(), path)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "failed to join path",
|
||||
slog.Error(err),
|
||||
slog.F("provider", providerName),
|
||||
slog.F("prefix", prov.RoutePrefix()),
|
||||
slog.F("path", path),
|
||||
)
|
||||
return nil, xerrors.Errorf("failed to configure provider '%v': failed to join bridged path: %w", providerName, err)
|
||||
}
|
||||
mux.Handle(route, handler)
|
||||
}
|
||||
|
||||
// Any requests which passthrough to this will be reverse-proxied to the upstream.
|
||||
//
|
||||
// We have to whitelist the known-safe routes because an API key with elevated privileges (i.e. admin) might be
|
||||
// configured, so we should just reverse-proxy known-safe routes.
|
||||
ftr := newPassthroughRouter(prov, logger.Named(fmt.Sprintf("passthrough.%s", prov.Name())), m, tracer)
|
||||
for _, path := range prov.PassthroughRoutes() {
|
||||
route, err := url.JoinPath(prov.RoutePrefix(), path)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "failed to join path",
|
||||
slog.Error(err),
|
||||
slog.F("provider", providerName),
|
||||
slog.F("prefix", prov.RoutePrefix()),
|
||||
slog.F("path", path),
|
||||
)
|
||||
return nil, xerrors.Errorf("failed to configure provider '%v': failed to join passed through path: %w", providerName, err)
|
||||
}
|
||||
mux.Handle(route, http.StripPrefix(prov.RoutePrefix(), ftr))
|
||||
}
|
||||
}
|
||||
|
||||
// Catch-all.
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
logger.Warn(r.Context(), "route not supported", slog.F("path", r.URL.Path), slog.F("method", r.Method))
|
||||
http.Error(w, fmt.Sprintf("route not supported: %s %s", r.Method, r.URL.Path), http.StatusNotFound)
|
||||
})
|
||||
|
||||
inflightCtx, cancel := context.WithCancel(context.Background())
|
||||
return &RequestBridge{
|
||||
mux: mux,
|
||||
logger: logger,
|
||||
mcpProxy: mcpProxy,
|
||||
inflightCtx: inflightCtx,
|
||||
inflightCancel: cancel,
|
||||
|
||||
closed: make(chan struct{}, 1),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// newInterceptionProcessor returns an [http.HandlerFunc] which is capable of creating a new interceptor and processing a given request
|
||||
// using [Provider] p, recording all usage events using [Recorder] rec.
|
||||
// If cbs is non-nil, circuit breaker protection is applied per endpoint/model tuple.
|
||||
func newInterceptionProcessor(p provider.Provider, cbs *circuitbreaker.ProviderCircuitBreakers, rec recorder.Recorder, mcpProxy mcp.ServerProxier, logger slog.Logger, m *metrics.Metrics, tracer trace.Tracer) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := tracer.Start(r.Context(), "Intercept")
|
||||
defer span.End()
|
||||
|
||||
// We execute this before CreateInterceptor since the interceptors
|
||||
// read the request body and don't reset them.
|
||||
client := GuessClient(r)
|
||||
sessionID := GuessSessionID(client, r)
|
||||
|
||||
interceptor, err := p.CreateInterceptor(w, r.WithContext(ctx), tracer)
|
||||
if err != nil {
|
||||
span.SetStatus(codes.Error, fmt.Sprintf("failed to create interceptor: %v", err))
|
||||
logger.Warn(ctx, "failed to create interceptor", slog.Error(err), slog.F("path", r.URL.Path))
|
||||
http.Error(w, fmt.Sprintf("failed to create %q interceptor", r.URL.Path), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if m != nil {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
m.InterceptionDuration.WithLabelValues(p.Name(), interceptor.Model()).Observe(time.Since(start).Seconds())
|
||||
}()
|
||||
}
|
||||
|
||||
actor := aibcontext.ActorFromContext(ctx)
|
||||
if actor == nil {
|
||||
logger.Warn(ctx, "no actor found in context")
|
||||
http.Error(w, "no actor found", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
traceAttrs := interceptor.TraceAttributes(r)
|
||||
span.SetAttributes(traceAttrs...)
|
||||
ctx = tracing.WithInterceptionAttributesInContext(ctx, traceAttrs)
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
// Record usage in the background to not block request flow.
|
||||
asyncRecorder := recorder.NewAsyncRecorder(logger, rec, recordingTimeout)
|
||||
asyncRecorder.WithMetrics(m)
|
||||
asyncRecorder.WithProvider(p.Name())
|
||||
asyncRecorder.WithModel(interceptor.Model())
|
||||
asyncRecorder.WithInitiatorID(actor.ID)
|
||||
asyncRecorder.WithClient(string(client))
|
||||
interceptor.Setup(logger, asyncRecorder, mcpProxy)
|
||||
|
||||
cred := interceptor.Credential()
|
||||
if err := rec.RecordInterception(ctx, &recorder.InterceptionRecord{
|
||||
ID: interceptor.ID().String(),
|
||||
InitiatorID: actor.ID,
|
||||
Metadata: actor.Metadata,
|
||||
Model: interceptor.Model(),
|
||||
Provider: p.Type(),
|
||||
ProviderName: p.Name(),
|
||||
UserAgent: r.UserAgent(),
|
||||
Client: string(client),
|
||||
ClientSessionID: sessionID,
|
||||
CorrelatingToolCallID: interceptor.CorrelatingToolCallID(),
|
||||
CredentialKind: string(cred.Kind),
|
||||
CredentialHint: cred.Hint,
|
||||
}); err != nil {
|
||||
span.SetStatus(codes.Error, fmt.Sprintf("failed to record interception: %v", err))
|
||||
logger.Warn(ctx, "failed to record interception", slog.Error(err))
|
||||
http.Error(w, "failed to record interception", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
route := strings.TrimPrefix(r.URL.Path, fmt.Sprintf("/%s", p.Name()))
|
||||
log := logger.With(
|
||||
slog.F("route", route),
|
||||
slog.F("provider", p.Name()),
|
||||
slog.F("interception_id", interceptor.ID()),
|
||||
slog.F("user_agent", r.UserAgent()),
|
||||
slog.F("streaming", interceptor.Streaming()),
|
||||
slog.F("credential_kind", string(cred.Kind)),
|
||||
slog.F("credential_hint", cred.Hint),
|
||||
slog.F("credential_length", cred.Length),
|
||||
)
|
||||
|
||||
log.Debug(ctx, "interception started")
|
||||
if m != nil {
|
||||
m.InterceptionsInflight.WithLabelValues(p.Name(), interceptor.Model(), route).Add(1)
|
||||
defer func() {
|
||||
m.InterceptionsInflight.WithLabelValues(p.Name(), interceptor.Model(), route).Sub(1)
|
||||
}()
|
||||
}
|
||||
|
||||
// Process request with circuit breaker protection if configured
|
||||
if err := cbs.Execute(route, interceptor.Model(), w, func(rw http.ResponseWriter) error {
|
||||
return interceptor.ProcessRequest(rw, r)
|
||||
}); err != nil {
|
||||
if m != nil {
|
||||
m.InterceptionCount.WithLabelValues(p.Name(), interceptor.Model(), metrics.InterceptionCountStatusFailed, route, r.Method, actor.ID, string(client)).Add(1)
|
||||
}
|
||||
span.SetStatus(codes.Error, fmt.Sprintf("interception failed: %v", err))
|
||||
log.Warn(ctx, "interception failed", slog.Error(err))
|
||||
} else {
|
||||
if m != nil {
|
||||
m.InterceptionCount.WithLabelValues(p.Name(), interceptor.Model(), metrics.InterceptionCountStatusCompleted, route, r.Method, actor.ID, string(client)).Add(1)
|
||||
}
|
||||
log.Debug(ctx, "interception ended")
|
||||
}
|
||||
|
||||
_ = asyncRecorder.RecordInterceptionEnded(ctx, &recorder.InterceptionRecordEnded{ID: interceptor.ID().String()})
|
||||
|
||||
// Ensure all recording have completed before completing request.
|
||||
asyncRecorder.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
// ServeHTTP exposes the internal http.Handler, which has all [Provider]s' routes registered.
|
||||
// It also tracks inflight requests.
|
||||
func (b *RequestBridge) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
select {
|
||||
case <-b.closed:
|
||||
http.Error(rw, "server closed", http.StatusInternalServerError)
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
// We want to abide by the context passed in without losing any of its
|
||||
// functionality, but we still want to link our shutdown context to each
|
||||
// request.
|
||||
ctx := mergeContexts(r.Context(), b.inflightCtx)
|
||||
|
||||
b.inflightReqs.Add(1)
|
||||
b.inflightWG.Add(1)
|
||||
defer func() {
|
||||
b.inflightReqs.Add(-1)
|
||||
b.inflightWG.Done()
|
||||
}()
|
||||
|
||||
b.mux.ServeHTTP(rw, r.WithContext(ctx))
|
||||
}
|
||||
|
||||
// Shutdown will attempt to gracefully shutdown. This entails waiting for all requests to
|
||||
// complete, and shutting down the MCP server proxier.
|
||||
// TODO: add tests.
|
||||
func (b *RequestBridge) Shutdown(ctx context.Context) error {
|
||||
var err error
|
||||
b.shutdownOnce.Do(func() {
|
||||
// Prevent any new requests from being accepted.
|
||||
close(b.closed)
|
||||
|
||||
// Wait for inflight requests to complete or context cancellation.
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
b.inflightWG.Wait()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Cancel all inflight requests, if any are still running.
|
||||
b.logger.Debug(ctx, "shutdown context canceled; canceling inflight requests", slog.Error(ctx.Err()))
|
||||
b.inflightCancel()
|
||||
<-done
|
||||
err = ctx.Err()
|
||||
case <-done:
|
||||
}
|
||||
|
||||
if b.mcpProxy != nil {
|
||||
// It's ok that we reuse the ctx here even if it's done, since the
|
||||
// Shutdown method will just immediately use the more aggressive close
|
||||
// since the ctx is already expired.
|
||||
err = multierror.Append(err, b.mcpProxy.Shutdown(ctx))
|
||||
}
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *RequestBridge) InflightRequests() int32 {
|
||||
return b.inflightReqs.Load()
|
||||
}
|
||||
|
||||
// mergeContexts merges two contexts together, so that if either is canceled
|
||||
// the returned context is canceled. The context values will only be used from
|
||||
// the first context.
|
||||
func mergeContexts(base, other context.Context) context.Context {
|
||||
ctx, cancel := context.WithCancel(base)
|
||||
go func() {
|
||||
defer cancel()
|
||||
select {
|
||||
case <-base.Done():
|
||||
case <-other.Done():
|
||||
}
|
||||
}()
|
||||
return ctx
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
package aibridge_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/otel"
|
||||
|
||||
"cdr.dev/slog/v3/sloggers/slogtest"
|
||||
"github.com/coder/coder/v2/aibridge"
|
||||
"github.com/coder/coder/v2/aibridge/config"
|
||||
"github.com/coder/coder/v2/aibridge/internal/testutil"
|
||||
"github.com/coder/coder/v2/aibridge/provider"
|
||||
)
|
||||
|
||||
var bridgeTestTracer = otel.Tracer("bridge_test")
|
||||
|
||||
func TestValidateProviders(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
logger := slogtest.Make(t, nil)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
providers []provider.Provider
|
||||
expectErr string
|
||||
}{
|
||||
{
|
||||
name: "all_supported_providers",
|
||||
providers: []provider.Provider{
|
||||
aibridge.NewOpenAIProvider(config.OpenAI{Name: "openai", BaseURL: "https://api.openai.com/v1/"}),
|
||||
aibridge.NewAnthropicProvider(config.Anthropic{Name: "anthropic", BaseURL: "https://api.anthropic.com/"}, nil),
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot", BaseURL: "https://api.individual.githubcopilot.com"}),
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot-business", BaseURL: "https://api.business.githubcopilot.com"}),
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot-enterprise", BaseURL: "https://api.enterprise.githubcopilot.com"}),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default_names_and_base_urls",
|
||||
providers: []provider.Provider{
|
||||
aibridge.NewOpenAIProvider(config.OpenAI{}),
|
||||
aibridge.NewAnthropicProvider(config.Anthropic{}, nil),
|
||||
aibridge.NewCopilotProvider(config.Copilot{}),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple_copilot_instances",
|
||||
providers: []provider.Provider{
|
||||
aibridge.NewCopilotProvider(config.Copilot{}),
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot-business", BaseURL: "https://api.business.githubcopilot.com"}),
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot-enterprise", BaseURL: "https://api.enterprise.githubcopilot.com"}),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "name_with_slashes",
|
||||
providers: []provider.Provider{
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot/business", BaseURL: "https://api.business.githubcopilot.com"}),
|
||||
},
|
||||
expectErr: "invalid provider name",
|
||||
},
|
||||
{
|
||||
name: "name_with_spaces",
|
||||
providers: []provider.Provider{
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot business", BaseURL: "https://api.business.githubcopilot.com"}),
|
||||
},
|
||||
expectErr: "invalid provider name",
|
||||
},
|
||||
{
|
||||
name: "name_with_uppercase",
|
||||
providers: []provider.Provider{
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "Copilot", BaseURL: "https://api.business.githubcopilot.com"}),
|
||||
},
|
||||
expectErr: "invalid provider name",
|
||||
},
|
||||
{
|
||||
name: "unique_names",
|
||||
providers: []provider.Provider{
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot", BaseURL: "https://api.individual.githubcopilot.com"}),
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot-business", BaseURL: "https://api.business.githubcopilot.com"}),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "duplicate_base_url_different_names",
|
||||
providers: []provider.Provider{
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot", BaseURL: "https://api.individual.githubcopilot.com"}),
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot-business", BaseURL: "https://api.individual.githubcopilot.com"}),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "duplicate_name",
|
||||
providers: []provider.Provider{
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot", BaseURL: "https://api.individual.githubcopilot.com"}),
|
||||
aibridge.NewCopilotProvider(config.Copilot{Name: "copilot", BaseURL: "https://api.business.githubcopilot.com"}),
|
||||
},
|
||||
expectErr: "duplicate provider name",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := aibridge.NewRequestBridge(t.Context(), tc.providers, nil, nil, logger, nil, bridgeTestTracer)
|
||||
if tc.expectErr != "" {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tc.expectErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPassthroughRoutesForProviders(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
upstreamRespBody := "upstream response"
|
||||
tests := []struct {
|
||||
name string
|
||||
baseURLPath string
|
||||
requestPath string
|
||||
provider func(string) provider.Provider
|
||||
expectPath string
|
||||
}{
|
||||
{
|
||||
name: "openAI_no_base_path",
|
||||
requestPath: "/openai/v1/conversations",
|
||||
provider: func(baseURL string) provider.Provider {
|
||||
return aibridge.NewOpenAIProvider(config.OpenAI{BaseURL: baseURL})
|
||||
},
|
||||
expectPath: "/conversations",
|
||||
},
|
||||
{
|
||||
name: "openAI_with_base_path",
|
||||
baseURLPath: "/v1",
|
||||
requestPath: "/openai/v1/conversations",
|
||||
provider: func(baseURL string) provider.Provider {
|
||||
return aibridge.NewOpenAIProvider(config.OpenAI{BaseURL: baseURL})
|
||||
},
|
||||
expectPath: "/v1/conversations",
|
||||
},
|
||||
{
|
||||
name: "anthropic_no_base_path",
|
||||
requestPath: "/anthropic/v1/models",
|
||||
provider: func(baseURL string) provider.Provider {
|
||||
return aibridge.NewAnthropicProvider(config.Anthropic{BaseURL: baseURL}, nil)
|
||||
},
|
||||
expectPath: "/v1/models",
|
||||
},
|
||||
{
|
||||
name: "anthropic_with_base_path",
|
||||
baseURLPath: "/v1",
|
||||
requestPath: "/anthropic/v1/models",
|
||||
provider: func(baseURL string) provider.Provider {
|
||||
return aibridge.NewAnthropicProvider(config.Anthropic{BaseURL: baseURL}, nil)
|
||||
},
|
||||
expectPath: "/v1/v1/models",
|
||||
},
|
||||
{
|
||||
name: "copilot_no_base_path",
|
||||
requestPath: "/copilot/models",
|
||||
provider: func(baseURL string) provider.Provider {
|
||||
return aibridge.NewCopilotProvider(config.Copilot{BaseURL: baseURL})
|
||||
},
|
||||
expectPath: "/models",
|
||||
},
|
||||
{
|
||||
name: "copilot_with_base_path",
|
||||
baseURLPath: "/v1",
|
||||
requestPath: "/copilot/models",
|
||||
provider: func(baseURL string) provider.Provider {
|
||||
return aibridge.NewCopilotProvider(config.Copilot{BaseURL: baseURL})
|
||||
},
|
||||
expectPath: "/v1/models",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
logger := slogtest.Make(t, nil)
|
||||
|
||||
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, tc.expectPath, r.URL.Path)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte(upstreamRespBody))
|
||||
}))
|
||||
t.Cleanup(upstream.Close)
|
||||
|
||||
rec := testutil.MockRecorder{}
|
||||
prov := tc.provider(upstream.URL + tc.baseURLPath)
|
||||
bridge, err := aibridge.NewRequestBridge(t.Context(), []provider.Provider{prov}, &rec, nil, logger, nil, bridgeTestTracer)
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest("", tc.requestPath, nil)
|
||||
resp := httptest.NewRecorder()
|
||||
bridge.ServeHTTP(resp, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
assert.Contains(t, resp.Body.String(), upstreamRespBody)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package circuitbreaker
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sony/gobreaker/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/aibridge/config"
|
||||
"github.com/coder/coder/v2/aibridge/metrics"
|
||||
)
|
||||
|
||||
// ErrCircuitOpen is returned by Execute when the circuit breaker is open
|
||||
// and the request was rejected without calling the handler.
|
||||
var ErrCircuitOpen = xerrors.New("circuit breaker is open")
|
||||
|
||||
// DefaultIsFailure returns true for standard HTTP status codes that typically
|
||||
// indicate upstream overload.
|
||||
func DefaultIsFailure(statusCode int) bool {
|
||||
switch statusCode {
|
||||
case http.StatusTooManyRequests, // 429
|
||||
http.StatusServiceUnavailable, // 503
|
||||
http.StatusGatewayTimeout: // 504
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ProviderCircuitBreakers manages per-endpoint/model circuit breakers for a single provider.
|
||||
type ProviderCircuitBreakers struct {
|
||||
provider string
|
||||
config config.CircuitBreaker
|
||||
breakers sync.Map // "endpoint:model" -> *gobreaker.CircuitBreaker[struct{}]
|
||||
onChange func(endpoint, model string, from, to gobreaker.State)
|
||||
metrics *metrics.Metrics
|
||||
}
|
||||
|
||||
// NewProviderCircuitBreakers creates circuit breakers for a single provider.
|
||||
// Returns nil if cfg is nil (no circuit breaker protection).
|
||||
// onChange is called when circuit state changes.
|
||||
// metrics is used to record circuit breaker reject counts (can be nil).
|
||||
func NewProviderCircuitBreakers(provider string, cfg *config.CircuitBreaker, onChange func(endpoint, model string, from, to gobreaker.State), m *metrics.Metrics) *ProviderCircuitBreakers {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
return &ProviderCircuitBreakers{
|
||||
provider: provider,
|
||||
config: *cfg,
|
||||
onChange: onChange,
|
||||
metrics: m,
|
||||
}
|
||||
}
|
||||
|
||||
// isFailure checks if the status code should count as a failure.
|
||||
// Falls back to DefaultIsFailure if no custom function is configured.
|
||||
func (p *ProviderCircuitBreakers) isFailure(statusCode int) bool {
|
||||
if p.config.IsFailure != nil {
|
||||
return p.config.IsFailure(statusCode)
|
||||
}
|
||||
return DefaultIsFailure(statusCode)
|
||||
}
|
||||
|
||||
// openErrBody returns the error response body when the circuit is open.
|
||||
func (p *ProviderCircuitBreakers) openErrBody() []byte {
|
||||
if p.config.OpenErrorResponse != nil {
|
||||
return p.config.OpenErrorResponse()
|
||||
}
|
||||
return []byte(`{"error":"circuit breaker is open"}`)
|
||||
}
|
||||
|
||||
// Get returns the circuit breaker for an endpoint/model tuple, creating it if needed.
|
||||
func (p *ProviderCircuitBreakers) Get(endpoint, model string) *gobreaker.CircuitBreaker[struct{}] {
|
||||
key := endpoint + ":" + model
|
||||
if v, ok := p.breakers.Load(key); ok {
|
||||
return v.(*gobreaker.CircuitBreaker[struct{}]) //nolint:forcetypeassert // sync.Map always stores this type
|
||||
}
|
||||
|
||||
settings := gobreaker.Settings{
|
||||
Name: p.provider + ":" + key,
|
||||
MaxRequests: p.config.MaxRequests,
|
||||
Interval: p.config.Interval,
|
||||
Timeout: p.config.Timeout,
|
||||
ReadyToTrip: func(counts gobreaker.Counts) bool {
|
||||
return counts.ConsecutiveFailures >= p.config.FailureThreshold
|
||||
},
|
||||
OnStateChange: func(_ string, from, to gobreaker.State) {
|
||||
if p.onChange != nil {
|
||||
p.onChange(endpoint, model, from, to)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cb := gobreaker.NewCircuitBreaker[struct{}](settings)
|
||||
actual, _ := p.breakers.LoadOrStore(key, cb)
|
||||
return actual.(*gobreaker.CircuitBreaker[struct{}]) //nolint:forcetypeassert // sync.Map always stores this type
|
||||
}
|
||||
|
||||
// statusCapturingWriter wraps http.ResponseWriter to capture the status code.
|
||||
// It implements http.Flusher to support streaming and http.Hijacker to
|
||||
// satisfy the FullResponseWriter lint rule.
|
||||
type statusCapturingWriter struct {
|
||||
http.ResponseWriter
|
||||
statusCode int
|
||||
headerWritten bool
|
||||
}
|
||||
|
||||
func (w *statusCapturingWriter) WriteHeader(code int) {
|
||||
if !w.headerWritten {
|
||||
w.statusCode = code
|
||||
w.headerWritten = true
|
||||
}
|
||||
w.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (w *statusCapturingWriter) Write(b []byte) (int, error) {
|
||||
if !w.headerWritten {
|
||||
w.statusCode = http.StatusOK
|
||||
w.headerWritten = true
|
||||
}
|
||||
return w.ResponseWriter.Write(b)
|
||||
}
|
||||
|
||||
func (w *statusCapturingWriter) Flush() {
|
||||
if f, ok := w.ResponseWriter.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *statusCapturingWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
h, ok := w.ResponseWriter.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, nil, xerrors.New("upstream ResponseWriter does not support hijacking")
|
||||
}
|
||||
return h.Hijack()
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying ResponseWriter for interface checks.
|
||||
func (w *statusCapturingWriter) Unwrap() http.ResponseWriter {
|
||||
return w.ResponseWriter
|
||||
}
|
||||
|
||||
// Execute runs the given handler function within circuit breaker protection.
|
||||
// If the circuit is open, the request is rejected with a 503 response, metrics are recorded,
|
||||
// and ErrCircuitOpen is returned.
|
||||
// Otherwise, it returns the handler's error (or nil on success).
|
||||
// The handler receives a wrapped ResponseWriter that captures the status code.
|
||||
// If the receiver is nil (no circuit breaker configured), the handler is called directly.
|
||||
func (p *ProviderCircuitBreakers) Execute(endpoint, model string, w http.ResponseWriter, handler func(http.ResponseWriter) error) error {
|
||||
if p == nil {
|
||||
return handler(w)
|
||||
}
|
||||
|
||||
cb := p.Get(endpoint, model)
|
||||
|
||||
// Wrap response writer to capture status code
|
||||
sw := &statusCapturingWriter{ResponseWriter: w, statusCode: http.StatusOK}
|
||||
|
||||
var handlerErr error
|
||||
_, err := cb.Execute(func() (struct{}, error) {
|
||||
handlerErr = handler(sw)
|
||||
if p.isFailure(sw.statusCode) {
|
||||
return struct{}{}, xerrors.Errorf("upstream error: %d", sw.statusCode)
|
||||
}
|
||||
return struct{}{}, nil
|
||||
})
|
||||
|
||||
if errors.Is(err, gobreaker.ErrOpenState) || errors.Is(err, gobreaker.ErrTooManyRequests) {
|
||||
if p.metrics != nil {
|
||||
p.metrics.CircuitBreakerRejects.WithLabelValues(p.provider, endpoint, model).Inc()
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Retry-After", fmt.Sprintf("%d", int64(p.config.Timeout.Seconds())))
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
_, _ = w.Write(p.openErrBody())
|
||||
return ErrCircuitOpen
|
||||
}
|
||||
|
||||
return handlerErr
|
||||
}
|
||||
|
||||
// Timeout returns the configured timeout duration for this circuit breaker.
|
||||
func (p *ProviderCircuitBreakers) Timeout() time.Duration {
|
||||
return p.config.Timeout
|
||||
}
|
||||
|
||||
// Provider returns the provider name for this circuit breaker.
|
||||
func (p *ProviderCircuitBreakers) Provider() string {
|
||||
return p.provider
|
||||
}
|
||||
|
||||
// OpenErrorResponse returns the error response body when the circuit is open.
|
||||
// This is exposed for handlers to use when responding to rejected requests.
|
||||
func (p *ProviderCircuitBreakers) OpenErrorResponse() []byte {
|
||||
return p.openErrBody()
|
||||
}
|
||||
|
||||
// StateToGaugeValue converts gobreaker.State to a gauge value.
|
||||
// closed=0, half-open=0.5, open=1
|
||||
func StateToGaugeValue(s gobreaker.State) float64 {
|
||||
switch s {
|
||||
case gobreaker.StateClosed:
|
||||
return 0
|
||||
case gobreaker.StateHalfOpen:
|
||||
return 0.5
|
||||
case gobreaker.StateOpen:
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
package circuitbreaker_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sony/gobreaker/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/coder/coder/v2/aibridge/circuitbreaker"
|
||||
"github.com/coder/coder/v2/aibridge/config"
|
||||
)
|
||||
|
||||
func TestExecute_PerModelIsolation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
sonnetCalls := atomic.Int32{}
|
||||
haikuCalls := atomic.Int32{}
|
||||
|
||||
cbs := circuitbreaker.NewProviderCircuitBreakers("test", &config.CircuitBreaker{
|
||||
FailureThreshold: 1,
|
||||
Interval: time.Minute,
|
||||
Timeout: time.Minute,
|
||||
MaxRequests: 1,
|
||||
}, func(endpoint, model string, from, to gobreaker.State) {}, nil)
|
||||
|
||||
endpoint := "/v1/messages"
|
||||
sonnetModel := "claude-sonnet-4-20250514"
|
||||
haikuModel := "claude-3-5-haiku-20241022"
|
||||
|
||||
// Trip circuit on sonnet model (returns 429)
|
||||
w := httptest.NewRecorder()
|
||||
err := cbs.Execute(endpoint, sonnetModel, w, func(rw http.ResponseWriter) error {
|
||||
sonnetCalls.Add(1)
|
||||
rw.WriteHeader(http.StatusTooManyRequests)
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int32(1), sonnetCalls.Load())
|
||||
|
||||
// Second sonnet request should be blocked by circuit breaker
|
||||
w = httptest.NewRecorder()
|
||||
err = cbs.Execute(endpoint, sonnetModel, w, func(rw http.ResponseWriter) error {
|
||||
sonnetCalls.Add(1)
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
return nil
|
||||
})
|
||||
assert.True(t, errors.Is(err, circuitbreaker.ErrCircuitOpen))
|
||||
assert.Equal(t, int32(1), sonnetCalls.Load()) // No new call
|
||||
assert.Equal(t, http.StatusServiceUnavailable, w.Code)
|
||||
|
||||
// Haiku model on same endpoint should still work (independent circuit)
|
||||
w = httptest.NewRecorder()
|
||||
err = cbs.Execute(endpoint, haikuModel, w, func(rw http.ResponseWriter) error {
|
||||
haikuCalls.Add(1)
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int32(1), haikuCalls.Load())
|
||||
}
|
||||
|
||||
func TestExecute_PerEndpointIsolation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
messagesCalls := atomic.Int32{}
|
||||
completionsCalls := atomic.Int32{}
|
||||
|
||||
cbs := circuitbreaker.NewProviderCircuitBreakers("test", &config.CircuitBreaker{
|
||||
FailureThreshold: 1,
|
||||
Interval: time.Minute,
|
||||
Timeout: time.Minute,
|
||||
MaxRequests: 1,
|
||||
}, func(endpoint, model string, from, to gobreaker.State) {}, nil)
|
||||
|
||||
model := "test-model"
|
||||
|
||||
// Trip circuit on /v1/messages endpoint (returns 429)
|
||||
w := httptest.NewRecorder()
|
||||
err := cbs.Execute("/v1/messages", model, w, func(rw http.ResponseWriter) error {
|
||||
messagesCalls.Add(1)
|
||||
rw.WriteHeader(http.StatusTooManyRequests)
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int32(1), messagesCalls.Load())
|
||||
|
||||
// Second /v1/messages request should be blocked
|
||||
w = httptest.NewRecorder()
|
||||
err = cbs.Execute("/v1/messages", model, w, func(rw http.ResponseWriter) error {
|
||||
messagesCalls.Add(1)
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
return nil
|
||||
})
|
||||
assert.True(t, errors.Is(err, circuitbreaker.ErrCircuitOpen))
|
||||
assert.Equal(t, int32(1), messagesCalls.Load()) // No new call
|
||||
assert.Equal(t, http.StatusServiceUnavailable, w.Code)
|
||||
|
||||
// /v1/chat/completions on same model should still work (different endpoint)
|
||||
w = httptest.NewRecorder()
|
||||
err = cbs.Execute("/v1/chat/completions", model, w, func(rw http.ResponseWriter) error {
|
||||
completionsCalls.Add(1)
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int32(1), completionsCalls.Load())
|
||||
}
|
||||
|
||||
func TestExecute_CustomIsFailure(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var calls atomic.Int32
|
||||
|
||||
// Custom IsFailure that treats 502 as failure
|
||||
cbs := circuitbreaker.NewProviderCircuitBreakers("test", &config.CircuitBreaker{
|
||||
FailureThreshold: 1,
|
||||
Interval: time.Minute,
|
||||
Timeout: time.Minute,
|
||||
MaxRequests: 1,
|
||||
IsFailure: func(statusCode int) bool {
|
||||
return statusCode == http.StatusBadGateway
|
||||
},
|
||||
}, func(endpoint, model string, from, to gobreaker.State) {}, nil)
|
||||
|
||||
// First request returns 502, trips circuit
|
||||
w := httptest.NewRecorder()
|
||||
err := cbs.Execute("/v1/messages", "test-model", w, func(rw http.ResponseWriter) error {
|
||||
calls.Add(1)
|
||||
rw.WriteHeader(http.StatusBadGateway)
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int32(1), calls.Load())
|
||||
|
||||
// Second request should be blocked
|
||||
w = httptest.NewRecorder()
|
||||
err = cbs.Execute("/v1/messages", "test-model", w, func(rw http.ResponseWriter) error {
|
||||
calls.Add(1)
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
return nil
|
||||
})
|
||||
assert.True(t, errors.Is(err, circuitbreaker.ErrCircuitOpen))
|
||||
assert.Equal(t, int32(1), calls.Load()) // No new call
|
||||
assert.Equal(t, http.StatusServiceUnavailable, w.Code)
|
||||
}
|
||||
|
||||
func TestExecute_OnStateChange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var stateChanges []struct {
|
||||
endpoint string
|
||||
model string
|
||||
from gobreaker.State
|
||||
to gobreaker.State
|
||||
}
|
||||
|
||||
cbs := circuitbreaker.NewProviderCircuitBreakers("test", &config.CircuitBreaker{
|
||||
FailureThreshold: 1,
|
||||
Interval: time.Minute,
|
||||
Timeout: time.Minute,
|
||||
MaxRequests: 1,
|
||||
}, func(endpoint, model string, from, to gobreaker.State) {
|
||||
stateChanges = append(stateChanges, struct {
|
||||
endpoint string
|
||||
model string
|
||||
from gobreaker.State
|
||||
to gobreaker.State
|
||||
}{endpoint, model, from, to})
|
||||
}, nil)
|
||||
|
||||
endpoint := "/v1/messages"
|
||||
model := "claude-sonnet-4-20250514"
|
||||
|
||||
// Trip circuit
|
||||
w := httptest.NewRecorder()
|
||||
err := cbs.Execute(endpoint, model, w, func(rw http.ResponseWriter) error {
|
||||
rw.WriteHeader(http.StatusTooManyRequests)
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify state change callback was called with correct parameters
|
||||
assert.Len(t, stateChanges, 1)
|
||||
assert.Equal(t, endpoint, stateChanges[0].endpoint)
|
||||
assert.Equal(t, model, stateChanges[0].model)
|
||||
assert.Equal(t, gobreaker.StateClosed, stateChanges[0].from)
|
||||
assert.Equal(t, gobreaker.StateOpen, stateChanges[0].to)
|
||||
}
|
||||
|
||||
func TestDefaultIsFailure(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
statusCode int
|
||||
isFailure bool
|
||||
}{
|
||||
{http.StatusOK, false},
|
||||
{http.StatusBadRequest, false},
|
||||
{http.StatusUnauthorized, false},
|
||||
{http.StatusTooManyRequests, true}, // 429
|
||||
{http.StatusInternalServerError, false},
|
||||
{http.StatusBadGateway, false},
|
||||
{http.StatusServiceUnavailable, true}, // 503
|
||||
{http.StatusGatewayTimeout, true}, // 504
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(t, tt.isFailure, circuitbreaker.DefaultIsFailure(tt.statusCode), "status code %d", tt.statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateToGaugeValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert.Equal(t, float64(0), circuitbreaker.StateToGaugeValue(gobreaker.StateClosed))
|
||||
assert.Equal(t, float64(0.5), circuitbreaker.StateToGaugeValue(gobreaker.StateHalfOpen))
|
||||
assert.Equal(t, float64(1), circuitbreaker.StateToGaugeValue(gobreaker.StateOpen))
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package aibridge
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Client string
|
||||
|
||||
const (
|
||||
// Possible values for the "client" field in interception records.
|
||||
// Must be kept in sync with documentation: https://github.com/coder/coder/blob/90c11f3386578da053ec5cd9f1475835b980e7c7/docs/ai-coder/ai-bridge/monitoring.md?plain=1#L36-L44
|
||||
ClientClaudeCode Client = "Claude Code"
|
||||
ClientCodex Client = "Codex"
|
||||
ClientZed Client = "Zed"
|
||||
ClientCopilotVSC Client = "GitHub Copilot (VS Code)"
|
||||
ClientCopilotCLI Client = "GitHub Copilot (CLI)"
|
||||
ClientKilo Client = "Kilo Code"
|
||||
ClientCoderAgents Client = "Coder Agents"
|
||||
ClientCrush Client = "Charm Crush"
|
||||
ClientMux Client = "Mux"
|
||||
ClientRoo Client = "Roo Code"
|
||||
ClientCursor Client = "Cursor"
|
||||
ClientUnknown Client = "Unknown"
|
||||
)
|
||||
|
||||
// GuessClient attempts to guess the client application from the request headers.
|
||||
// Not all clients set proper user agent headers, so this is a best-effort approach.
|
||||
// Based on https://github.com/coder/aibridge/issues/20#issuecomment-3769444101.
|
||||
func GuessClient(r *http.Request) Client {
|
||||
userAgent := strings.ToLower(r.UserAgent())
|
||||
originator := r.Header.Get("originator")
|
||||
|
||||
// Must be kept in sync with documentation: https://github.com/coder/coder/blob/90c11f3386578da053ec5cd9f1475835b980e7c7/docs/ai-coder/ai-bridge/monitoring.md?plain=1#L36-L44
|
||||
switch {
|
||||
case strings.HasPrefix(userAgent, "mux/"):
|
||||
return ClientMux
|
||||
case strings.HasPrefix(userAgent, "claude"):
|
||||
return ClientClaudeCode
|
||||
case strings.HasPrefix(userAgent, "codex"):
|
||||
return ClientCodex
|
||||
case strings.HasPrefix(userAgent, "zed/"):
|
||||
return ClientZed
|
||||
case strings.HasPrefix(userAgent, "githubcopilotchat/"):
|
||||
return ClientCopilotVSC
|
||||
case strings.HasPrefix(userAgent, "copilot/"):
|
||||
return ClientCopilotCLI
|
||||
case strings.HasPrefix(userAgent, "kilo-code/") || originator == "kilo-code":
|
||||
return ClientKilo
|
||||
case strings.HasPrefix(userAgent, "roo-code/") || originator == "roo-code":
|
||||
return ClientRoo
|
||||
case strings.HasPrefix(userAgent, "coder-agents/"):
|
||||
return ClientCoderAgents
|
||||
case strings.HasPrefix(userAgent, "charm crush/"):
|
||||
return ClientCrush
|
||||
case r.Header.Get("x-cursor-client-version") != "":
|
||||
return ClientCursor
|
||||
}
|
||||
return ClientUnknown
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package aibridge_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/aibridge"
|
||||
)
|
||||
|
||||
func TestGuessClient(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
userAgent string
|
||||
headers map[string]string
|
||||
wantClient aibridge.Client
|
||||
}{
|
||||
{
|
||||
name: "mux",
|
||||
userAgent: "mux/0.19.0-next.2.gcceff159 ai-sdk/openai/3.0.36 ai-sdk/provider-utils/4.0.15 runtime/node.js/22",
|
||||
wantClient: aibridge.ClientMux,
|
||||
},
|
||||
{
|
||||
name: "claude_code",
|
||||
userAgent: "claude-cli/2.0.67 (external, cli)",
|
||||
wantClient: aibridge.ClientClaudeCode,
|
||||
},
|
||||
{
|
||||
name: "codex_cli",
|
||||
userAgent: "codex_cli_rs/0.87.0 (Mac OS 26.2.0; arm64) ghostty/1.3.0-main_250877ef",
|
||||
wantClient: aibridge.ClientCodex,
|
||||
},
|
||||
{
|
||||
name: "zed",
|
||||
userAgent: "Zed/0.219.4+stable.119.abc123 (macos; aarch64)",
|
||||
wantClient: aibridge.ClientZed,
|
||||
},
|
||||
{
|
||||
name: "github_copilot_vsc",
|
||||
userAgent: "GitHubCopilotChat/0.37.2026011603",
|
||||
wantClient: aibridge.ClientCopilotVSC,
|
||||
},
|
||||
{
|
||||
name: "github_copilot_cli",
|
||||
userAgent: "copilot/0.0.403 (client/cli linux v24.11.1)",
|
||||
wantClient: aibridge.ClientCopilotCLI,
|
||||
},
|
||||
{
|
||||
name: "kilo_code_user_agent",
|
||||
userAgent: "kilo-code/5.1.0 (darwin 25.2.0; arm64) node/22.21.1",
|
||||
wantClient: aibridge.ClientKilo,
|
||||
},
|
||||
{
|
||||
name: "kilo_code_originator",
|
||||
headers: map[string]string{"Originator": "kilo-code"},
|
||||
wantClient: aibridge.ClientKilo,
|
||||
},
|
||||
{
|
||||
name: "roo_code_user_agent",
|
||||
userAgent: "roo-code/3.45.0 (darwin 25.2.0; arm64) node/22.21.1",
|
||||
wantClient: aibridge.ClientRoo,
|
||||
},
|
||||
{
|
||||
name: "roo_code_originator",
|
||||
headers: map[string]string{"Originator": "roo-code"},
|
||||
wantClient: aibridge.ClientRoo,
|
||||
},
|
||||
{
|
||||
name: "coder_agents",
|
||||
userAgent: "coder-agents/v2.24.0 (linux/amd64)",
|
||||
wantClient: aibridge.ClientCoderAgents,
|
||||
},
|
||||
{
|
||||
name: "coder_agents_dev",
|
||||
userAgent: "coder-agents/v0.0.0-devel (darwin/arm64)",
|
||||
wantClient: aibridge.ClientCoderAgents,
|
||||
},
|
||||
{
|
||||
name: "charm_crush",
|
||||
userAgent: "Charm Crush/0.1.11",
|
||||
wantClient: aibridge.ClientCrush,
|
||||
},
|
||||
{
|
||||
name: "cursor_x_cursor_client_version",
|
||||
userAgent: "connect-es/1.6.1",
|
||||
headers: map[string]string{"X-Cursor-client-version": "0.50.0"},
|
||||
wantClient: aibridge.ClientCursor,
|
||||
},
|
||||
{
|
||||
name: "cursor_x_cursor_some_other_header",
|
||||
headers: map[string]string{"x-cursor-client-version": "abc123"},
|
||||
wantClient: aibridge.ClientCursor,
|
||||
},
|
||||
{
|
||||
name: "unknown_client",
|
||||
userAgent: "ccclaude-cli/calude-with-wrong-prefix",
|
||||
wantClient: aibridge.ClientUnknown,
|
||||
},
|
||||
{
|
||||
name: "empty_user_agent",
|
||||
userAgent: "",
|
||||
wantClient: aibridge.ClientUnknown,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
req.Header.Set("User-Agent", tt.userAgent)
|
||||
for key, value := range tt.headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
|
||||
got := aibridge.GuessClient(req)
|
||||
require.Equal(t, tt.wantClient, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package config
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
ProviderAnthropic = "anthropic"
|
||||
ProviderOpenAI = "openai"
|
||||
ProviderCopilot = "copilot"
|
||||
)
|
||||
|
||||
type Anthropic struct {
|
||||
// Name is the provider instance name. If empty, defaults to "anthropic".
|
||||
Name string
|
||||
BaseURL string
|
||||
Key string
|
||||
APIDumpDir string
|
||||
CircuitBreaker *CircuitBreaker
|
||||
SendActorHeaders bool
|
||||
ExtraHeaders map[string]string
|
||||
// BYOKBearerToken is set in BYOK mode when the user authenticates
|
||||
// with a access token. When set, the access token is used for upstream
|
||||
// LLM requests instead of the API key.
|
||||
BYOKBearerToken string
|
||||
}
|
||||
|
||||
type AWSBedrock struct {
|
||||
Region string
|
||||
AccessKey, AccessKeySecret string
|
||||
Model, SmallFastModel string
|
||||
// If set, requests will be sent to this URL instead of the default AWS Bedrock endpoint
|
||||
// (https://bedrock-runtime.{region}.amazonaws.com).
|
||||
// This is useful for routing requests through a proxy or for testing.
|
||||
BaseURL string
|
||||
}
|
||||
|
||||
type OpenAI struct {
|
||||
// Name is the provider instance name. If empty, defaults to "openai".
|
||||
Name string
|
||||
BaseURL string
|
||||
Key string
|
||||
APIDumpDir string
|
||||
CircuitBreaker *CircuitBreaker
|
||||
SendActorHeaders bool
|
||||
ExtraHeaders map[string]string
|
||||
}
|
||||
|
||||
type Copilot struct {
|
||||
// Name is the provider instance name. If empty, defaults to "copilot".
|
||||
Name string
|
||||
BaseURL string
|
||||
APIDumpDir string
|
||||
CircuitBreaker *CircuitBreaker
|
||||
}
|
||||
|
||||
// CircuitBreaker holds configuration for circuit breakers.
|
||||
type CircuitBreaker struct {
|
||||
// MaxRequests is the maximum number of requests allowed in half-open state.
|
||||
MaxRequests uint32
|
||||
// Interval is the cyclic period of the closed state for clearing internal counts.
|
||||
Interval time.Duration
|
||||
// Timeout is how long the circuit stays open before transitioning to half-open.
|
||||
Timeout time.Duration
|
||||
// FailureThreshold is the number of consecutive failures that triggers the circuit to open.
|
||||
FailureThreshold uint32
|
||||
// IsFailure determines if a status code should count as a failure.
|
||||
// If nil, defaults to DefaultIsFailure.
|
||||
IsFailure func(statusCode int) bool
|
||||
// OpenErrorResponse returns the response body when the circuit is open.
|
||||
// This should match the provider's error format.
|
||||
OpenErrorResponse func() []byte
|
||||
}
|
||||
|
||||
// DefaultCircuitBreaker returns sensible defaults for circuit breaker configuration.
|
||||
func DefaultCircuitBreaker() CircuitBreaker {
|
||||
return CircuitBreaker{
|
||||
FailureThreshold: 5,
|
||||
Interval: 10 * time.Second,
|
||||
Timeout: 30 * time.Second,
|
||||
MaxRequests: 3,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/coder/coder/v2/aibridge/recorder"
|
||||
)
|
||||
|
||||
type (
|
||||
actorContextKey struct{}
|
||||
)
|
||||
|
||||
type Actor struct {
|
||||
ID string
|
||||
Metadata recorder.Metadata
|
||||
}
|
||||
|
||||
func AsActor(ctx context.Context, actorID string, metadata recorder.Metadata) context.Context {
|
||||
return context.WithValue(ctx, actorContextKey{}, &Actor{ID: actorID, Metadata: metadata})
|
||||
}
|
||||
|
||||
func ActorFromContext(ctx context.Context) *Actor {
|
||||
a, ok := ctx.Value(actorContextKey{}).(*Actor)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// ActorIDFromContext safely extracts the actor ID from the context.
|
||||
// Returns an empty string if no actor is found.
|
||||
func ActorIDFromContext(ctx context.Context) string {
|
||||
if actor := ActorFromContext(ctx); actor != nil {
|
||||
return actor.ID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package context_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
aibcontext "github.com/coder/coder/v2/aibridge/context"
|
||||
"github.com/coder/coder/v2/aibridge/recorder"
|
||||
)
|
||||
|
||||
func TestAsActor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given: a metadata map
|
||||
metadata := recorder.Metadata{"key": "value"}
|
||||
|
||||
// When: storing an actor in the context
|
||||
ctx := aibcontext.AsActor(context.Background(), "actor-123", metadata)
|
||||
|
||||
// Then: the actor should be retrievable with correct ID and metadata
|
||||
actor := aibcontext.ActorFromContext(ctx)
|
||||
require.NotNil(t, actor)
|
||||
assert.Equal(t, "actor-123", actor.ID)
|
||||
assert.Equal(t, "value", actor.Metadata["key"])
|
||||
}
|
||||
|
||||
func TestActorFromContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("returns actor when present", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given: a context with an actor
|
||||
ctx := aibcontext.AsActor(context.Background(), "test-id", recorder.Metadata{})
|
||||
|
||||
// When: extracting the actor from context
|
||||
actor := aibcontext.ActorFromContext(ctx)
|
||||
|
||||
// Then: the actor should be returned with correct ID
|
||||
require.NotNil(t, actor)
|
||||
assert.Equal(t, "test-id", actor.ID)
|
||||
})
|
||||
|
||||
t.Run("returns nil when no actor", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given: a context without an actor
|
||||
ctx := context.Background()
|
||||
|
||||
// When: extracting the actor from context
|
||||
actor := aibcontext.ActorFromContext(ctx)
|
||||
|
||||
// Then: nil should be returned
|
||||
assert.Nil(t, actor)
|
||||
})
|
||||
}
|
||||
|
||||
func TestActorIDFromContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("returns actor ID when present", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given: a context with an actor
|
||||
ctx := aibcontext.AsActor(context.Background(), "test-actor-id", recorder.Metadata{})
|
||||
|
||||
// When: extracting the actor ID from context
|
||||
got := aibcontext.ActorIDFromContext(ctx)
|
||||
|
||||
// Then: the actor ID should be returned
|
||||
assert.Equal(t, "test-actor-id", got)
|
||||
})
|
||||
|
||||
t.Run("returns empty string when no actor", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Given: a context without an actor
|
||||
ctx := context.Background()
|
||||
|
||||
// When: extracting the actor ID from context
|
||||
got := aibcontext.ActorIDFromContext(ctx)
|
||||
|
||||
// Then: an empty string should be returned
|
||||
assert.Empty(t, got)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
API endpoints not explicitly handled will fallthrough to upstream via reverse-proxy.
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"type": "model",
|
||||
"id": "claude-opus-4-1-20250805",
|
||||
"display_name": "Claude Opus 4.1",
|
||||
"created_at": "2025-08-05T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"type": "model",
|
||||
"id": "claude-opus-4-20250514",
|
||||
"display_name": "Claude Opus 4",
|
||||
"created_at": "2025-05-22T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"type": "model",
|
||||
"id": "claude-sonnet-4-20250514",
|
||||
"display_name": "Claude Sonnet 4",
|
||||
"created_at": "2025-05-22T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"type": "model",
|
||||
"id": "claude-3-7-sonnet-20250219",
|
||||
"display_name": "Claude Sonnet 3.7",
|
||||
"created_at": "2025-02-24T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"type": "model",
|
||||
"id": "claude-3-5-sonnet-20241022",
|
||||
"display_name": "Claude Sonnet 3.5 (New)",
|
||||
"created_at": "2024-10-22T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"type": "model",
|
||||
"id": "claude-3-5-haiku-20241022",
|
||||
"display_name": "Claude Haiku 3.5",
|
||||
"created_at": "2024-10-22T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"type": "model",
|
||||
"id": "claude-3-5-sonnet-20240620",
|
||||
"display_name": "Claude Sonnet 3.5 (Old)",
|
||||
"created_at": "2024-06-20T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"type": "model",
|
||||
"id": "claude-3-haiku-20240307",
|
||||
"display_name": "Claude Haiku 3",
|
||||
"created_at": "2024-03-07T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"type": "model",
|
||||
"id": "claude-3-opus-20240229",
|
||||
"display_name": "Claude Opus 3",
|
||||
"created_at": "2024-02-29T00:00:00Z"
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"first_id": "claude-opus-4-1-20250805",
|
||||
"last_id": "claude-3-opus-20240229"
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
Simple request using a Haiku model (small/fast model).
|
||||
Used to validate that prompts are captured for small/fast models like Haiku,
|
||||
which Claude Code uses for ancillary tasks (e.g. generating session titles,
|
||||
push notification summaries).
|
||||
|
||||
-- request --
|
||||
{
|
||||
"max_tokens": 8192,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "how many angels can dance on the head of a pin\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"model": "claude-haiku-4-5",
|
||||
"temperature": 1
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: message_start
|
||||
data: {"type":"message_start","message":{"id":"msg_01Pvyf26bY17RcjmWfJsXGBn","type":"message","role":"assistant","model":"claude-haiku-4-5-20251001","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":18,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":1,"service_tier":"standard"}} }
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"This is a classic philosophical question about medieval scholasticism. I'll give a thoughtful answer."}}
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":0}
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""} }
|
||||
|
||||
event: ping
|
||||
data: {"type": "ping"}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"This"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" is a famous philosophical question often used to illustrate medieval"}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" scholastic debates that seem pointless or ov"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"erly abstract. The question \"How many angels can dance on the head of"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" a pin?\" is typically cited as an example of us"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"eless speculation.\n\nHistorically, medieval theolog"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"ians did debate the nature of angels -"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" whether they were incorporeal beings, how"}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" they occupied space, and whether multiple angels could exist"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" in the same location. However, there"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"'s little evidence they literally"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" debated dancing angels on pinheads.\n\nThe question has"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" no factual answer since it depends on assumptions about:"}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"\n- The existence and nature of angels\n- Whether"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" incorporeal beings occupy physical space\n- What"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" constitutes \"dancing\" for a spiritual"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" entity\n- The size of both the"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" pin and the angels\n\nIt's become a metaph"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"or for overthinking trivial matters"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" or getting lost in theoretical discussions disconnected from practical reality."} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" Some use it to critique certain types of academic"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" or theological debate, while others defen"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"d the value of exploring fundamental questions about existence an"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"d metaphysics.\n\nSo while u"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"nanswerable literally, it serves as an interesting lens"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" for discussing the nature of philosophical inquiry itself."} }
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":1 }
|
||||
|
||||
event: message_delta
|
||||
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":240} }
|
||||
|
||||
event: message_stop
|
||||
data: {"type":"message_stop" }
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "msg_01Pvyf26bY17RcjmWfJsXGBn",
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"model": "claude-haiku-4-5-20251001",
|
||||
"content": [
|
||||
{
|
||||
"type": "thinking",
|
||||
"thinking": "This is a classic philosophical question about medieval scholasticism. I'll give a thoughtful answer."
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "This is a famous philosophical question, often called \"How many angels can dance on the head of a pin?\" It's typically used to represent pointless or overly abstract theological debates.\n\nThe question doesn't have a literal answer because:\n\n1. **Historical context**: It's often attributed to medieval scholastic philosophers, though there's little evidence they actually debated this exact question. It became a popular way to mock what some saw as useless academic arguments.\n\n2. **Philosophical purpose**: The question highlights the difficulty of discussing non-physical beings (angels) in physical terms (space on a pinhead).\n\n3. **Different interpretations**: \n - If angels are purely spiritual, they might not take up physical space at all\n - If they do occupy space, we'd need to know their \"size\"\n - The question might be asking about the nature of space, matter, and spirit\n\nSo the real answer is that it's not meant to be answered literally - it's a thought experiment about the limits of rational inquiry and the sometimes absurd directions theological speculation can take.\n\nWould you like to explore the philosophical implications behind this question, or were you thinking about it in a different context?"
|
||||
}
|
||||
],
|
||||
"stop_reason": "end_turn",
|
||||
"stop_sequence": null,
|
||||
"usage": {
|
||||
"input_tokens": 18,
|
||||
"cache_creation_input_tokens": 0,
|
||||
"cache_read_input_tokens": 0,
|
||||
"output_tokens": 254,
|
||||
"service_tier": "standard"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
Claude Code has builtin tools to (e.g.) explore the filesystem.
|
||||
This fixture has two thinking blocks before the tool_use block.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"max_tokens": 1024,
|
||||
"tools": [
|
||||
{
|
||||
"name": "Read",
|
||||
"description": "Read the contents of a file at the given path.",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "The absolute path to the file to read"
|
||||
}
|
||||
},
|
||||
"required": ["file_path"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "read the foo file"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: message_start
|
||||
data: {"type":"message_start","message":{"id":"msg_015SQewixvT9s4cABCVvUE6g","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":22,"cache_read_input_tokens":13993,"output_tokens":5,"service_tier":"standard"}} }
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"The user wants me to read a file called \"foo\". Let me find and read it."}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"signature_delta","signature":"Eu8BCkYICxgCKkBR++kFr7Za2JhF/9OCpjEc46/EcipL75RK+MEbxJ/VBJPWQTWrNGfwb5khWYJtKEpjjkH07cR/MQvThfb7t7CkEgwU4pKwL7NuZXd1/wgaDILyd0bYMqQovWo3dyIw95Ny7yZPljNBDLsvMBdBr7w+RtbU+AlSftjBuBZHp0VzI54/W+9u6f7qfx0JXsVBKldqqOjFvewT8Xm6Qp/77g6/j0zBiuAQABj/6vS1qATjd8KSIFDg9G/tCtzwmV/T/egmzswWd5CBiAhW6lgJgEDRr+gRUrFSOB7o3hypW8FUnUrr1JtzzwMYAQ=="}}
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":0}
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":1,"content_block":{"type":"thinking","thinking":""}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"thinking_delta","thinking":"I should use the Read tool to access the file contents."}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"signature_delta","signature":"Aa1BCkYICxgCKkBR++kFr7Za2JhF/9OCpjEc46/EcipL75RK+MEbxJ/VBJPWQTWrNGfwb5khWYJtKEpjjkH07cR/MQvThfb7t7CkEgwU4pKwL7NuZXd1/wgaDILyd0bYMqQovWo3dyIw95Ny7yZPljNBDLsvMBdBr7w+RtbU+AlSftjBuBZHp0VzI54/W+9u6f7qfx0JXsVBKldqqOjFvewT8Xm6Qp/77g6/j0zBiuAQABj/6vS1qATjd8KSIFDg9G/tCtzwmV/T/egmzswWd5CBiAhW6lgJgEDRr+gRUrFSOB7o3hypW8FUnUrr1JtzzwMYAQ=="}}
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":1}
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":2,"content_block":{"type":"tool_use","id":"toolu_01RX68weRSquLx6HUTj65iBo","name":"Read","input":{}}}
|
||||
|
||||
event: ping
|
||||
data: {"type": "ping"}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":""} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":"{\"file_path\": \"/tmp/blah/foo"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":"\"}"} }
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":2 }
|
||||
|
||||
event: message_delta
|
||||
data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":61} }
|
||||
|
||||
event: message_stop
|
||||
data: {"type":"message_stop" }
|
||||
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "msg_01JHKqEmh7wYuPXqUWUvusfL",
|
||||
"container": {
|
||||
"id": "",
|
||||
"expires_at": "0001-01-01T00:00:00Z"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"type": "thinking",
|
||||
"thinking": "The user wants me to read a file called \"foo\". Let me find and read it.",
|
||||
"signature": "Eu8BCkYICxgCKkBR++kFr7Za2JhF/9OCpjEc46/EcipL75RK+MEbxJ/VBJPWQTWrNGfwb5khWYJtKEpjjkH07cR/MQvThfb7t7CkEgwU4pKwL7NuZXd1/wgaDILyd0bYMqQovWo3dyIw95Ny7yZPljNBDLsvMBdBr7w+RtbU+AlSftjBuBZHp0VzI54/W+9u6f7qfx0JXsVBKldqqOjFvewT8Xm6Qp/77g6/j0zBiuAQABj/6vS1qATjd8KSIFDg9G/tCtzwmV/T/egmzswWd5CBiAhW6lgJgEDRr+gRUrFSOB7o3hypW8FUnUrr1JtzzwMYAQ=="
|
||||
},
|
||||
{
|
||||
"type": "thinking",
|
||||
"thinking": "I should use the Read tool to access the file contents.",
|
||||
"signature": "Aa1BCkYICxgCKkBR++kFr7Za2JhF/9OCpjEc46/EcipL75RK+MEbxJ/VBJPWQTWrNGfwb5khWYJtKEpjjkH07cR/MQvThfb7t7CkEgwU4pKwL7NuZXd1/wgaDILyd0bYMqQovWo3dyIw95Ny7yZPljNBDLsvMBdBr7w+RtbU+AlSftjBuBZHp0VzI54/W+9u6f7qfx0JXsVBKldqqOjFvewT8Xm6Qp/77g6/j0zBiuAQABj/6vS1qATjd8KSIFDg9G/tCtzwmV/T/egmzswWd5CBiAhW6lgJgEDRr+gRUrFSOB7o3hypW8FUnUrr1JtzzwMYAQ=="
|
||||
},
|
||||
{
|
||||
"citations": null,
|
||||
"text": "",
|
||||
"type": "tool_use",
|
||||
"id": "toolu_01AusGgY5aKFhzWrFBv9JfHq",
|
||||
"input": {
|
||||
"file_path": "/tmp/blah/foo"
|
||||
},
|
||||
"name": "Read",
|
||||
"content": {
|
||||
"OfWebSearchResultBlockArray": null,
|
||||
"OfString": "",
|
||||
"OfMCPToolResultBlockContent": null,
|
||||
"error_code": "",
|
||||
"type": "",
|
||||
"content": null,
|
||||
"return_code": 0,
|
||||
"stderr": "",
|
||||
"stdout": ""
|
||||
},
|
||||
"tool_use_id": "",
|
||||
"server_name": "",
|
||||
"is_error": false,
|
||||
"file_id": "",
|
||||
"signature": "",
|
||||
"thinking": "",
|
||||
"data": ""
|
||||
}
|
||||
],
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"role": "assistant",
|
||||
"stop_reason": "tool_use",
|
||||
"stop_sequence": "",
|
||||
"type": "message",
|
||||
"usage": {
|
||||
"cache_creation": {
|
||||
"ephemeral_1h_input_tokens": 0,
|
||||
"ephemeral_5m_input_tokens": 0
|
||||
},
|
||||
"cache_creation_input_tokens": 0,
|
||||
"cache_read_input_tokens": 23490,
|
||||
"input_tokens": 5,
|
||||
"output_tokens": 84,
|
||||
"server_tool_use": {
|
||||
"web_search_requests": 0
|
||||
},
|
||||
"service_tier": "standard"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
Simple request + error which occurs before streaming begins (where applicable).
|
||||
|
||||
-- request --
|
||||
{
|
||||
"max_tokens": 8192,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "yo"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"model": "claude-sonnet-4-0",
|
||||
"temperature": 1
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
HTTP/2.0 400 Bad Request
|
||||
Content-Length: 164
|
||||
Content-Type: application/json
|
||||
|
||||
{"type":"error","error":{"type":"invalid_request_error","message":"prompt is too long: 205429 tokens > 200000 maximum"},"request_id":"req_011CV5Jab6gR3ZNs9Sj6apiD"}
|
||||
|
||||
|
||||
-- non-streaming --
|
||||
HTTP/2.0 400 Bad Request
|
||||
Content-Length: 164
|
||||
Content-Type: application/json
|
||||
|
||||
{"type":"error","error":{"type":"invalid_request_error","message":"prompt is too long: 205429 tokens > 200000 maximum"},"request_id":"req_011CV5Jab6gR3ZNs9Sj6apiD"}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
Simple request.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"max_tokens": 8192,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "how many angels can dance on the head of a pin\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"model": "claude-sonnet-4-0",
|
||||
"temperature": 1
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: message_start
|
||||
data: {"type":"message_start","message":{"id":"msg_01Pvyf26bY17RcjmWfJsXGBn","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":18,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":1,"service_tier":"standard"}} }
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"This is a classic philosophical question about medieval scholasticism. I'll give a thoughtful answer."}}
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":0}
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""} }
|
||||
|
||||
event: ping
|
||||
data: {"type": "ping"}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"This"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" is a famous philosophical question often used to illustrate medieval"}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" scholastic debates that seem pointless or ov"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"erly abstract. The question \"How many angels can dance on the head of"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" a pin?\" is typically cited as an example of us"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"eless speculation.\n\nHistorically, medieval theolog"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"ians did debate the nature of angels -"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" whether they were incorporeal beings, how"}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" they occupied space, and whether multiple angels could exist"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" in the same location. However, there"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"'s little evidence they literally"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" debated dancing angels on pinheads.\n\nThe question has"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" no factual answer since it depends on assumptions about:"}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"\n- The existence and nature of angels\n- Whether"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" incorporeal beings occupy physical space\n- What"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" constitutes \"dancing\" for a spiritual"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" entity\n- The size of both the"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" pin and the angels\n\nIt's become a metaph"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"or for overthinking trivial matters"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" or getting lost in theoretical discussions disconnected from practical reality."} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" Some use it to critique certain types of academic"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" or theological debate, while others defen"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"d the value of exploring fundamental questions about existence an"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"d metaphysics.\n\nSo while u"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"nanswerable literally, it serves as an interesting lens"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":" for discussing the nature of philosophical inquiry itself."} }
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":1 }
|
||||
|
||||
event: message_delta
|
||||
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":240} }
|
||||
|
||||
event: message_stop
|
||||
data: {"type":"message_stop" }
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "msg_01Pvyf26bY17RcjmWfJsXGBn",
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"content": [
|
||||
{
|
||||
"type": "thinking",
|
||||
"thinking": "This is a classic philosophical question about medieval scholasticism. I'll give a thoughtful answer."
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "This is a famous philosophical question, often called \"How many angels can dance on the head of a pin?\" It's typically used to represent pointless or overly abstract theological debates.\n\nThe question doesn't have a literal answer because:\n\n1. **Historical context**: It's often attributed to medieval scholastic philosophers, though there's little evidence they actually debated this exact question. It became a popular way to mock what some saw as useless academic arguments.\n\n2. **Philosophical purpose**: The question highlights the difficulty of discussing non-physical beings (angels) in physical terms (space on a pinhead).\n\n3. **Different interpretations**: \n - If angels are purely spiritual, they might not take up physical space at all\n - If they do occupy space, we'd need to know their \"size\"\n - The question might be asking about the nature of space, matter, and spirit\n\nSo the real answer is that it's not meant to be answered literally - it's a thought experiment about the limits of rational inquiry and the sometimes absurd directions theological speculation can take.\n\nWould you like to explore the philosophical implications behind this question, or were you thinking about it in a different context?"
|
||||
}
|
||||
],
|
||||
"stop_reason": "end_turn",
|
||||
"stop_sequence": null,
|
||||
"usage": {
|
||||
"input_tokens": 18,
|
||||
"cache_creation_input_tokens": 0,
|
||||
"cache_read_input_tokens": 0,
|
||||
"output_tokens": 254,
|
||||
"service_tier": "standard"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
Simple Bedrock request. Tests that fields unsupported by Bedrock are removed
|
||||
and adaptive thinking is converted to enabled with a budget. Includes all
|
||||
bedrockUnsupportedFields (metadata, service_tier, container, inference_geo)
|
||||
and beta-gated fields (output_config, context_management).
|
||||
|
||||
-- request --
|
||||
{
|
||||
"model": "claude-sonnet-4-6",
|
||||
"max_tokens": 32000,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Hello."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"thinking": {"type": "adaptive"},
|
||||
"metadata": {"user_id": "session_abc123"},
|
||||
"service_tier": "auto",
|
||||
"container": {"type": "ephemeral"},
|
||||
"inference_geo": {"allow": ["us"]},
|
||||
"output_config": {"effort": "medium"},
|
||||
"context_management": {"edits": [{"type": "clear_thinking_20251015", "keep": "all"}]},
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: message_start
|
||||
data: {"type":"message_start","message":{"id":"msg_bdrk_01Test","type":"message","role":"assistant","model":"claude-sonnet-4-5-20250929","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":10,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":4}}}
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello! How can I help?"}}
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":0}
|
||||
|
||||
event: message_delta
|
||||
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":10}}
|
||||
|
||||
event: message_stop
|
||||
data: {"type":"message_stop"}
|
||||
|
||||
-- non-streaming --
|
||||
{"id":"msg_bdrk_01Test","type":"message","role":"assistant","model":"claude-sonnet-4-5-20250929","content":[{"type":"text","text":"Hello! How can I help?"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":10,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":10}}
|
||||
@@ -0,0 +1,181 @@
|
||||
Claude Code has builtin tools to (e.g.) explore the filesystem.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"max_tokens": 1024,
|
||||
"tools": [
|
||||
{
|
||||
"name": "Read",
|
||||
"description": "Read the contents of a file at the given path.",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "The absolute path to the file to read"
|
||||
}
|
||||
},
|
||||
"required": ["file_path"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "read the foo file"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: message_start
|
||||
data: {"type":"message_start","message":{"id":"msg_015SQewixvT9s4cABCVvUE6g","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":22,"cache_read_input_tokens":13993,"output_tokens":5,"service_tier":"standard"}} }
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"The user wants me to read"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":" a"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":" file called \""} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"foo\"."} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":" Let me find"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":" and"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":" read it."} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":""} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"signature_delta","signature":"Eu8BCkYICxgCKkBR++kFr7Za2JhF/9OCpjEc46/EcipL75RK+MEbxJ/VBJPWQTWrNGfwb5khWYJtKEpjjkH07cR/MQvThfb7t7CkEgwU4pKwL7NuZXd1/wgaDILyd0bYMqQovWo3dyIw95Ny7yZPljNBDLsvMBdBr7w+RtbU+AlSftjBuBZHp0VzI54/W+9u6f7qfx0JXsVBKldqqOjFvewT8Xm6Qp/77g6/j0zBiuAQABj/6vS1qATjd8KSIFDg9G/tCtzwmV/T/egmzswWd5CBiAhW6lgJgEDRr+gRUrFSOB7o3hypW8FUnUrr1JtzzwMYAQ=="}}
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":0}
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_01RX68weRSquLx6HUTj65iBo","name":"Read","input":{}}}
|
||||
|
||||
event: ping
|
||||
data: {"type": "ping"}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":""} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"file_path\": \"/tmp/blah/foo"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"\"}"} }
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":1 }
|
||||
|
||||
event: message_delta
|
||||
data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":61} }
|
||||
|
||||
event: message_stop
|
||||
data: {"type":"message_stop" }
|
||||
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "msg_01JHKqEmh7wYuPXqUWUvusfL",
|
||||
"container": {
|
||||
"id": "",
|
||||
"expires_at": "0001-01-01T00:00:00Z"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"type": "thinking",
|
||||
"thinking": "The user wants me to read a file called \"foo\". Let me find and read it.",
|
||||
"signature": "Eu8BCkYICxgCKkBR++kFr7Za2JhF/9OCpjEc46/EcipL75RK+MEbxJ/VBJPWQTWrNGfwb5khWYJtKEpjjkH07cR/MQvThfb7t7CkEgwU4pKwL7NuZXd1/wgaDILyd0bYMqQovWo3dyIw95Ny7yZPljNBDLsvMBdBr7w+RtbU+AlSftjBuBZHp0VzI54/W+9u6f7qfx0JXsVBKldqqOjFvewT8Xm6Qp/77g6/j0zBiuAQABj/6vS1qATjd8KSIFDg9G/tCtzwmV/T/egmzswWd5CBiAhW6lgJgEDRr+gRUrFSOB7o3hypW8FUnUrr1JtzzwMYAQ=="
|
||||
},
|
||||
{
|
||||
"citations": null,
|
||||
"text": "I can see there's a file named `foo` in the `/tmp/blah` directory. Let me read it.",
|
||||
"type": "text",
|
||||
"id": "",
|
||||
"input": null,
|
||||
"name": "",
|
||||
"content": {
|
||||
"OfWebSearchResultBlockArray": null,
|
||||
"OfString": "",
|
||||
"OfMCPToolResultBlockContent": null,
|
||||
"error_code": "",
|
||||
"type": "",
|
||||
"content": null,
|
||||
"return_code": 0,
|
||||
"stderr": "",
|
||||
"stdout": ""
|
||||
},
|
||||
"tool_use_id": "",
|
||||
"server_name": "",
|
||||
"is_error": false,
|
||||
"file_id": "",
|
||||
"signature": "",
|
||||
"thinking": "",
|
||||
"data": ""
|
||||
},
|
||||
{
|
||||
"citations": null,
|
||||
"text": "",
|
||||
"type": "tool_use",
|
||||
"id": "toolu_01AusGgY5aKFhzWrFBv9JfHq",
|
||||
"input": {
|
||||
"file_path": "/tmp/blah/foo"
|
||||
},
|
||||
"name": "Read",
|
||||
"content": {
|
||||
"OfWebSearchResultBlockArray": null,
|
||||
"OfString": "",
|
||||
"OfMCPToolResultBlockContent": null,
|
||||
"error_code": "",
|
||||
"type": "",
|
||||
"content": null,
|
||||
"return_code": 0,
|
||||
"stderr": "",
|
||||
"stdout": ""
|
||||
},
|
||||
"tool_use_id": "",
|
||||
"server_name": "",
|
||||
"is_error": false,
|
||||
"file_id": "",
|
||||
"signature": "",
|
||||
"thinking": "",
|
||||
"data": ""
|
||||
}
|
||||
],
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"role": "assistant",
|
||||
"stop_reason": "tool_use",
|
||||
"stop_sequence": "",
|
||||
"type": "message",
|
||||
"usage": {
|
||||
"cache_creation": {
|
||||
"ephemeral_1h_input_tokens": 0,
|
||||
"ephemeral_5m_input_tokens": 0
|
||||
},
|
||||
"cache_creation_input_tokens": 0,
|
||||
"cache_read_input_tokens": 23490,
|
||||
"input_tokens": 5,
|
||||
"output_tokens": 84,
|
||||
"server_tool_use": {
|
||||
"web_search_requests": 0
|
||||
},
|
||||
"service_tier": "standard"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
Claude Code has builtin tools to (e.g.) explore the filesystem.
|
||||
This fixture has a single thinking block followed by two parallel tool_use blocks.
|
||||
The thinking should only be attributed to the first tool_use.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"max_tokens": 1024,
|
||||
"tools": [
|
||||
{
|
||||
"name": "Read",
|
||||
"description": "Read the contents of a file at the given path.",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "The absolute path to the file to read"
|
||||
}
|
||||
},
|
||||
"required": ["file_path"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "read the foo and bar files"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: message_start
|
||||
data: {"type":"message_start","message":{"id":"msg_01ParallelToolStream","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":22,"cache_read_input_tokens":13993,"output_tokens":5,"service_tier":"standard"}} }
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":""}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"The user wants me to read two files: \"foo\" and \"bar\". I'll read both of them."}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"signature_delta","signature":"Eu8BCkYICxgCKkBR++kFr7Za2JhF/9OCpjEc46/EcipL75RK+MEbxJ/VBJPWQTWrNGfwb5khWYJtKEpjjkH07cR/MQvThfb7t7CkEgwU4pKwL7NuZXd1/wgaDILyd0bYMqQovWo3dyIw95Ny7yZPljNBDLsvMBdBr7w+RtbU+AlSftjBuBZHp0VzI54/W+9u6f7qfx0JXsVBKldqqOjFvewT8Xm6Qp/77g6/j0zBiuAQABj/6vS1qATjd8KSIFDg9G/tCtzwmV/T/egmzswWd5CBiAhW6lgJgEDRr+gRUrFSOB7o3hypW8FUnUrr1JtzzwMYAQ=="}}
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":0}
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_01ParallelFirst000000000","name":"Read","input":{}}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":""} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"file_path\": \"/tmp/blah/foo"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"\"}"} }
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":1 }
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":2,"content_block":{"type":"tool_use","id":"toolu_01ParallelSecond00000000","name":"Read","input":{}}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":""} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":"{\"file_path\": \"/tmp/blah/bar"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":2,"delta":{"type":"input_json_delta","partial_json":"\"}"} }
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":2 }
|
||||
|
||||
event: message_delta
|
||||
data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":72} }
|
||||
|
||||
event: message_stop
|
||||
data: {"type":"message_stop" }
|
||||
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "msg_01ParallelToolBlocking",
|
||||
"container": {
|
||||
"id": "",
|
||||
"expires_at": "0001-01-01T00:00:00Z"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"type": "thinking",
|
||||
"thinking": "The user wants me to read two files: \"foo\" and \"bar\". I'll read both of them.",
|
||||
"signature": "Eu8BCkYICxgCKkBR++kFr7Za2JhF/9OCpjEc46/EcipL75RK+MEbxJ/VBJPWQTWrNGfwb5khWYJtKEpjjkH07cR/MQvThfb7t7CkEgwU4pKwL7NuZXd1/wgaDILyd0bYMqQovWo3dyIw95Ny7yZPljNBDLsvMBdBr7w+RtbU+AlSftjBuBZHp0VzI54/W+9u6f7qfx0JXsVBKldqqOjFvewT8Xm6Qp/77g6/j0zBiuAQABj/6vS1qATjd8KSIFDg9G/tCtzwmV/T/egmzswWd5CBiAhW6lgJgEDRr+gRUrFSOB7o3hypW8FUnUrr1JtzzwMYAQ=="
|
||||
},
|
||||
{
|
||||
"citations": null,
|
||||
"text": "",
|
||||
"type": "tool_use",
|
||||
"id": "toolu_01ParallelBlockFirst0000",
|
||||
"input": {
|
||||
"file_path": "/tmp/blah/foo"
|
||||
},
|
||||
"name": "Read",
|
||||
"content": {
|
||||
"OfWebSearchResultBlockArray": null,
|
||||
"OfString": "",
|
||||
"OfMCPToolResultBlockContent": null,
|
||||
"error_code": "",
|
||||
"type": "",
|
||||
"content": null,
|
||||
"return_code": 0,
|
||||
"stderr": "",
|
||||
"stdout": ""
|
||||
},
|
||||
"tool_use_id": "",
|
||||
"server_name": "",
|
||||
"is_error": false,
|
||||
"file_id": "",
|
||||
"signature": "",
|
||||
"thinking": "",
|
||||
"data": ""
|
||||
},
|
||||
{
|
||||
"citations": null,
|
||||
"text": "",
|
||||
"type": "tool_use",
|
||||
"id": "toolu_01ParallelBlockSecond000",
|
||||
"input": {
|
||||
"file_path": "/tmp/blah/bar"
|
||||
},
|
||||
"name": "Read",
|
||||
"content": {
|
||||
"OfWebSearchResultBlockArray": null,
|
||||
"OfString": "",
|
||||
"OfMCPToolResultBlockContent": null,
|
||||
"error_code": "",
|
||||
"type": "",
|
||||
"content": null,
|
||||
"return_code": 0,
|
||||
"stderr": "",
|
||||
"stdout": ""
|
||||
},
|
||||
"tool_use_id": "",
|
||||
"server_name": "",
|
||||
"is_error": false,
|
||||
"file_id": "",
|
||||
"signature": "",
|
||||
"thinking": "",
|
||||
"data": ""
|
||||
}
|
||||
],
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"role": "assistant",
|
||||
"stop_reason": "tool_use",
|
||||
"stop_sequence": "",
|
||||
"type": "message",
|
||||
"usage": {
|
||||
"cache_creation": {
|
||||
"ephemeral_1h_input_tokens": 0,
|
||||
"ephemeral_5m_input_tokens": 0
|
||||
},
|
||||
"cache_creation_input_tokens": 0,
|
||||
"cache_read_input_tokens": 23490,
|
||||
"input_tokens": 5,
|
||||
"output_tokens": 95,
|
||||
"server_tool_use": {
|
||||
"web_search_requests": 0
|
||||
},
|
||||
"service_tier": "standard"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
Coder MCP tools automatically injected.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"max_tokens": 1024,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "list coder workspace IDs for admin"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: message_start
|
||||
data: {"type":"message_start","message":{"id":"msg_01JWGa2JHsKBHL28Cjr2dvPK","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":7545,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":1,"service_tier":"standard"}} }
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} }
|
||||
|
||||
event: ping
|
||||
data: {"type": "ping"}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"I'll list the work"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"spaces for the admin user to get their"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" workspace IDs."} }
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":0 }
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_01TSQLR6R6wBUqoxGPjQKDAj","name":"bmcp_coder_coder_list_workspaces","input":{}} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":""} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"owner\""} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":": \"ad"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"min\"}"} }
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":1 }
|
||||
|
||||
event: message_delta
|
||||
data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":74}}
|
||||
|
||||
event: message_stop
|
||||
data: {"type":"message_stop" }
|
||||
|
||||
|
||||
-- streaming/tool-call --
|
||||
event: message_start
|
||||
data: {"type":"message_start","message":{"id":"msg_01LZSVzMCLivzXrp6ZnTcmeG","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":7763,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":1,"service_tier":"standard"}} }
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} }
|
||||
|
||||
event: ping
|
||||
data: {"type": "ping"}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Here"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" are the workspace IDs for the admin user:\n\n**"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Admin's Workspaces:**\n- Workspace ID: `dd711d5c-83c"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"6-4c08-a0af-b73055906e8"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"c`\n - Name: `bob`\n - Template: `docker"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"`\n - Template ID: `b3a9d9b4-486a-4"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"f21-8884-d81d5dbdd837`"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"\n\nThe admin user currently has 1 workspace named \"bob\" created from"} }
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" the \"docker\" template."} }
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":0 }
|
||||
|
||||
event: message_delta
|
||||
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":128} }
|
||||
|
||||
event: message_stop
|
||||
data: {"type":"message_stop" }
|
||||
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "msg_01FwkWU26guw9EwkL8zeacPL",
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "I'll list the workspaces for the admin user to get their workspace IDs."
|
||||
},
|
||||
{
|
||||
"type": "tool_use",
|
||||
"id": "toolu_01QjNz5b3HxAqAccTVnSMsKP",
|
||||
"name": "bmcp_coder_coder_list_workspaces",
|
||||
"input": {
|
||||
"owner": "admin"
|
||||
}
|
||||
}
|
||||
],
|
||||
"stop_reason": "tool_use",
|
||||
"stop_sequence": null,
|
||||
"usage": {
|
||||
"input_tokens": 7545,
|
||||
"cache_creation_input_tokens": 0,
|
||||
"cache_read_input_tokens": 0,
|
||||
"output_tokens": 75,
|
||||
"service_tier": "standard"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-- non-streaming/tool-call --
|
||||
{
|
||||
"id": "msg_01Sr5BnPSwodTo8Df4XvUBg5",
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Here are the Coder workspace IDs for the admin user:\n\n**Workspace ID:** `dd711d5c-83c6-4c08-a0af-b73055906e8c`\n- **Name:** bob\n- **Template:** docker\n- **Template ID:** b3a9d9b4-486a-4f21-8884-d81d5dbdd837\n- **Status:** Up to date (not outdated)\n\nThe admin user currently has 1 workspace named \"bob\" running on the \"docker\" template."
|
||||
}
|
||||
],
|
||||
"stop_reason": "end_turn",
|
||||
"stop_sequence": null,
|
||||
"usage": {
|
||||
"input_tokens": 7763,
|
||||
"cache_creation_input_tokens": 0,
|
||||
"cache_read_input_tokens": 0,
|
||||
"output_tokens": 129,
|
||||
"service_tier": "standard"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Simple request + error.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"max_tokens": 8192,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "yo"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"model": "claude-sonnet-4-0",
|
||||
"temperature": 1,
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: message_start
|
||||
data: {"type":"message_start","message":{"id":"msg_01Pvyf26bY17RcjmWfJsXGBn","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":18,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":1,"service_tier":"standard"}} }
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} }
|
||||
|
||||
event: ping
|
||||
data: {"type": "ping"}
|
||||
|
||||
event: error
|
||||
data: {"type": "error", "error": {"type": "api_error", "message": "Overloaded"}}
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
package fixtures
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/tools/txtar"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed anthropic/simple.txtar
|
||||
AntSimple []byte
|
||||
|
||||
//go:embed anthropic/single_builtin_tool.txtar
|
||||
AntSingleBuiltinTool []byte
|
||||
|
||||
//go:embed anthropic/multi_thinking_builtin_tool.txtar
|
||||
AntMultiThinkingBuiltinTool []byte
|
||||
|
||||
//go:embed anthropic/single_builtin_tool_parallel.txtar
|
||||
AntSingleBuiltinToolParallel []byte
|
||||
|
||||
//go:embed anthropic/single_injected_tool.txtar
|
||||
AntSingleInjectedTool []byte
|
||||
|
||||
//go:embed anthropic/fallthrough.txtar
|
||||
AntFallthrough []byte
|
||||
|
||||
//go:embed anthropic/stream_error.txtar
|
||||
AntMidStreamError []byte
|
||||
|
||||
//go:embed anthropic/non_stream_error.txtar
|
||||
AntNonStreamError []byte
|
||||
|
||||
//go:embed anthropic/simple_bedrock.txtar
|
||||
AntSimpleBedrock []byte
|
||||
|
||||
//go:embed anthropic/haiku_simple.txtar
|
||||
AntHaikuSimple []byte
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed openai/chatcompletions/simple.txtar
|
||||
OaiChatSimple []byte
|
||||
|
||||
//go:embed openai/chatcompletions/single_builtin_tool.txtar
|
||||
OaiChatSingleBuiltinTool []byte
|
||||
|
||||
//go:embed openai/chatcompletions/single_injected_tool.txtar
|
||||
OaiChatSingleInjectedTool []byte
|
||||
|
||||
//go:embed openai/chatcompletions/fallthrough.txtar
|
||||
OaiChatFallthrough []byte
|
||||
|
||||
//go:embed openai/chatcompletions/stream_error.txtar
|
||||
OaiChatMidStreamError []byte
|
||||
|
||||
//go:embed openai/chatcompletions/non_stream_error.txtar
|
||||
OaiChatNonStreamError []byte
|
||||
|
||||
//go:embed openai/chatcompletions/streaming_injected_tool_no_preamble.txtar
|
||||
OaiChatStreamingInjectedToolNoPreamble []byte
|
||||
|
||||
//go:embed openai/chatcompletions/streaming_injected_tool_nonzero_index.txtar
|
||||
OaiChatStreamingInjectedToolNonzeroIndex []byte
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed openai/responses/blocking/simple.txtar
|
||||
OaiResponsesBlockingSimple []byte
|
||||
|
||||
//go:embed openai/responses/blocking/single_builtin_tool.txtar
|
||||
OaiResponsesBlockingSingleBuiltinTool []byte
|
||||
|
||||
//go:embed openai/responses/blocking/multi_reasoning_builtin_tool.txtar
|
||||
OaiResponsesBlockingMultiReasoningBuiltinTool []byte
|
||||
|
||||
//go:embed openai/responses/blocking/commentary_builtin_tool.txtar
|
||||
OaiResponsesBlockingCommentaryBuiltinTool []byte
|
||||
|
||||
//go:embed openai/responses/blocking/summary_and_commentary_builtin_tool.txtar
|
||||
OaiResponsesBlockingSummaryAndCommentaryBuiltinTool []byte
|
||||
|
||||
//go:embed openai/responses/blocking/cached_input_tokens.txtar
|
||||
OaiResponsesBlockingCachedInputTokens []byte
|
||||
|
||||
//go:embed openai/responses/blocking/custom_tool.txtar
|
||||
OaiResponsesBlockingCustomTool []byte
|
||||
|
||||
//go:embed openai/responses/blocking/conversation.txtar
|
||||
OaiResponsesBlockingConversation []byte
|
||||
|
||||
//go:embed openai/responses/blocking/http_error.txtar
|
||||
OaiResponsesBlockingHTTPErr []byte
|
||||
|
||||
//go:embed openai/responses/blocking/prev_response_id.txtar
|
||||
OaiResponsesBlockingPrevResponseID []byte
|
||||
|
||||
//go:embed openai/responses/blocking/single_builtin_tool_parallel.txtar
|
||||
OaiResponsesBlockingSingleBuiltinToolParallel []byte
|
||||
|
||||
//go:embed openai/responses/blocking/single_injected_tool.txtar
|
||||
OaiResponsesBlockingSingleInjectedTool []byte
|
||||
|
||||
//go:embed openai/responses/blocking/single_injected_tool_error.txtar
|
||||
OaiResponsesBlockingSingleInjectedToolError []byte
|
||||
|
||||
//go:embed openai/responses/blocking/wrong_response_format.txtar
|
||||
OaiResponsesBlockingWrongResponseFormat []byte
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed openai/responses/streaming/simple.txtar
|
||||
OaiResponsesStreamingSimple []byte
|
||||
|
||||
//go:embed openai/responses/streaming/codex_example.txtar
|
||||
OaiResponsesStreamingCodex []byte
|
||||
|
||||
//go:embed openai/responses/streaming/builtin_tool.txtar
|
||||
OaiResponsesStreamingBuiltinTool []byte
|
||||
|
||||
//go:embed openai/responses/streaming/multi_reasoning_builtin_tool.txtar
|
||||
OaiResponsesStreamingMultiReasoningBuiltinTool []byte
|
||||
|
||||
//go:embed openai/responses/streaming/commentary_builtin_tool.txtar
|
||||
OaiResponsesStreamingCommentaryBuiltinTool []byte
|
||||
|
||||
//go:embed openai/responses/streaming/summary_and_commentary_builtin_tool.txtar
|
||||
OaiResponsesStreamingSummaryAndCommentaryBuiltinTool []byte
|
||||
|
||||
//go:embed openai/responses/streaming/cached_input_tokens.txtar
|
||||
OaiResponsesStreamingCachedInputTokens []byte
|
||||
|
||||
//go:embed openai/responses/streaming/custom_tool.txtar
|
||||
OaiResponsesStreamingCustomTool []byte
|
||||
|
||||
//go:embed openai/responses/streaming/conversation.txtar
|
||||
OaiResponsesStreamingConversation []byte
|
||||
|
||||
//go:embed openai/responses/streaming/http_error.txtar
|
||||
OaiResponsesStreamingHTTPErr []byte
|
||||
|
||||
//go:embed openai/responses/streaming/prev_response_id.txtar
|
||||
OaiResponsesStreamingPrevResponseID []byte
|
||||
|
||||
//go:embed openai/responses/streaming/single_builtin_tool_parallel.txtar
|
||||
OaiResponsesStreamingSingleBuiltinToolParallel []byte
|
||||
|
||||
//go:embed openai/responses/streaming/single_injected_tool.txtar
|
||||
OaiResponsesStreamingSingleInjectedTool []byte
|
||||
|
||||
//go:embed openai/responses/streaming/single_injected_tool_error.txtar
|
||||
OaiResponsesStreamingSingleInjectedToolError []byte
|
||||
|
||||
//go:embed openai/responses/streaming/stream_error.txtar
|
||||
OaiResponsesStreamingStreamError []byte
|
||||
|
||||
//go:embed openai/responses/streaming/stream_failure.txtar
|
||||
OaiResponsesStreamingStreamFailure []byte
|
||||
|
||||
//go:embed openai/responses/streaming/wrong_response_format.txtar
|
||||
OaiResponsesStreamingWrongResponseFormat []byte
|
||||
)
|
||||
|
||||
// Section name constants matching the file names used in txtar fixtures.
|
||||
const (
|
||||
fileRequest = "request"
|
||||
fileStreamingResponse = "streaming"
|
||||
fileNonStreamingResponse = "non-streaming"
|
||||
fileStreamingToolCall = "streaming/tool-call"
|
||||
fileNonStreamingToolCall = "non-streaming/tool-call"
|
||||
|
||||
// Exported aliases so callers can check [Fixture.Has] before calling a
|
||||
// getter that would otherwise fail the test.
|
||||
SectionStreaming = fileStreamingResponse
|
||||
SectionNonStreaming = fileNonStreamingResponse
|
||||
SectionStreamingToolCall = fileStreamingToolCall
|
||||
SectionNonStreamToolCall = fileNonStreamingToolCall
|
||||
)
|
||||
|
||||
// Fixture holds the named sections of a parsed txtar test fixture.
|
||||
type Fixture struct {
|
||||
sections map[string][]byte
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
// Has reports whether the fixture contains the named section.
|
||||
func (f Fixture) Has(name string) bool {
|
||||
_, ok := f.sections[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (f Fixture) Request() []byte {
|
||||
f.t.Helper()
|
||||
v, ok := f.sections[fileRequest]
|
||||
require.True(f.t, ok, "fixture archive missing %q section", fileRequest)
|
||||
return v
|
||||
}
|
||||
|
||||
func (f Fixture) Streaming() []byte {
|
||||
f.t.Helper()
|
||||
v, ok := f.sections[fileStreamingResponse]
|
||||
require.True(f.t, ok, "fixture archive missing %q section", fileStreamingResponse)
|
||||
return v
|
||||
}
|
||||
|
||||
func (f Fixture) NonStreaming() []byte {
|
||||
f.t.Helper()
|
||||
v, ok := f.sections[fileNonStreamingResponse]
|
||||
require.True(f.t, ok, "fixture archive missing %q section", fileNonStreamingResponse)
|
||||
return v
|
||||
}
|
||||
|
||||
func (f Fixture) StreamingToolCall() []byte {
|
||||
f.t.Helper()
|
||||
v, ok := f.sections[fileStreamingToolCall]
|
||||
require.True(f.t, ok, "fixture archive missing %q section", fileStreamingToolCall)
|
||||
return v
|
||||
}
|
||||
|
||||
func (f Fixture) NonStreamingToolCall() []byte {
|
||||
f.t.Helper()
|
||||
v, ok := f.sections[fileNonStreamingToolCall]
|
||||
require.True(f.t, ok, "fixture archive missing %q section", fileNonStreamingToolCall)
|
||||
return v
|
||||
}
|
||||
|
||||
// Parse parses raw txtar data into a [Fixture].
|
||||
func Parse(t *testing.T, data []byte) Fixture {
|
||||
t.Helper()
|
||||
|
||||
archive := txtar.Parse(data)
|
||||
require.NotEmpty(t, archive.Files, "fixture archive has no files")
|
||||
|
||||
sections := make(map[string][]byte, len(archive.Files))
|
||||
for _, f := range archive.Files {
|
||||
sections[f.Name] = f.Data
|
||||
}
|
||||
return Fixture{sections: sections, t: t}
|
||||
}
|
||||
|
||||
// Request extracts the "request" fixture from raw txtar data.
|
||||
func Request(t *testing.T, fixture []byte) []byte {
|
||||
t.Helper()
|
||||
return Parse(t, fixture).Request()
|
||||
}
|
||||
@@ -0,0 +1,524 @@
|
||||
API endpoints not explicitly handled will fallthrough to upstream via reverse-proxy.
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "gpt-4-0613",
|
||||
"object": "model",
|
||||
"created": 1686588896,
|
||||
"owned_by": "openai"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4",
|
||||
"object": "model",
|
||||
"created": 1687882411,
|
||||
"owned_by": "openai"
|
||||
},
|
||||
{
|
||||
"id": "gpt-3.5-turbo",
|
||||
"object": "model",
|
||||
"created": 1677610602,
|
||||
"owned_by": "openai"
|
||||
},
|
||||
{
|
||||
"id": "gpt-5-nano",
|
||||
"object": "model",
|
||||
"created": 1754426384,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-5",
|
||||
"object": "model",
|
||||
"created": 1754425777,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-5-mini-2025-08-07",
|
||||
"object": "model",
|
||||
"created": 1754425867,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-5-mini",
|
||||
"object": "model",
|
||||
"created": 1754425928,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-5-nano-2025-08-07",
|
||||
"object": "model",
|
||||
"created": 1754426303,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "davinci-002",
|
||||
"object": "model",
|
||||
"created": 1692634301,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "babbage-002",
|
||||
"object": "model",
|
||||
"created": 1692634615,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-3.5-turbo-instruct",
|
||||
"object": "model",
|
||||
"created": 1692901427,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-3.5-turbo-instruct-0914",
|
||||
"object": "model",
|
||||
"created": 1694122472,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "dall-e-3",
|
||||
"object": "model",
|
||||
"created": 1698785189,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "dall-e-2",
|
||||
"object": "model",
|
||||
"created": 1698798177,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4-1106-preview",
|
||||
"object": "model",
|
||||
"created": 1698957206,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-3.5-turbo-1106",
|
||||
"object": "model",
|
||||
"created": 1698959748,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "tts-1-hd",
|
||||
"object": "model",
|
||||
"created": 1699046015,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "tts-1-1106",
|
||||
"object": "model",
|
||||
"created": 1699053241,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "tts-1-hd-1106",
|
||||
"object": "model",
|
||||
"created": 1699053533,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "text-embedding-3-small",
|
||||
"object": "model",
|
||||
"created": 1705948997,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "text-embedding-3-large",
|
||||
"object": "model",
|
||||
"created": 1705953180,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4-0125-preview",
|
||||
"object": "model",
|
||||
"created": 1706037612,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4-turbo-preview",
|
||||
"object": "model",
|
||||
"created": 1706037777,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-3.5-turbo-0125",
|
||||
"object": "model",
|
||||
"created": 1706048358,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4-turbo",
|
||||
"object": "model",
|
||||
"created": 1712361441,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4-turbo-2024-04-09",
|
||||
"object": "model",
|
||||
"created": 1712601677,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o",
|
||||
"object": "model",
|
||||
"created": 1715367049,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-2024-05-13",
|
||||
"object": "model",
|
||||
"created": 1715368132,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini-2024-07-18",
|
||||
"object": "model",
|
||||
"created": 1721172717,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini",
|
||||
"object": "model",
|
||||
"created": 1721172741,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-2024-08-06",
|
||||
"object": "model",
|
||||
"created": 1722814719,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "chatgpt-4o-latest",
|
||||
"object": "model",
|
||||
"created": 1723515131,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o1-mini-2024-09-12",
|
||||
"object": "model",
|
||||
"created": 1725648979,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o1-mini",
|
||||
"object": "model",
|
||||
"created": 1725649008,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-realtime-preview-2024-10-01",
|
||||
"object": "model",
|
||||
"created": 1727131766,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-audio-preview-2024-10-01",
|
||||
"object": "model",
|
||||
"created": 1727389042,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-audio-preview",
|
||||
"object": "model",
|
||||
"created": 1727460443,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-realtime-preview",
|
||||
"object": "model",
|
||||
"created": 1727659998,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "omni-moderation-latest",
|
||||
"object": "model",
|
||||
"created": 1731689265,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "omni-moderation-2024-09-26",
|
||||
"object": "model",
|
||||
"created": 1732734466,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-realtime-preview-2024-12-17",
|
||||
"object": "model",
|
||||
"created": 1733945430,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-audio-preview-2024-12-17",
|
||||
"object": "model",
|
||||
"created": 1734034239,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini-realtime-preview-2024-12-17",
|
||||
"object": "model",
|
||||
"created": 1734112601,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini-audio-preview-2024-12-17",
|
||||
"object": "model",
|
||||
"created": 1734115920,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o1-2024-12-17",
|
||||
"object": "model",
|
||||
"created": 1734326976,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o1",
|
||||
"object": "model",
|
||||
"created": 1734375816,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini-realtime-preview",
|
||||
"object": "model",
|
||||
"created": 1734387380,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini-audio-preview",
|
||||
"object": "model",
|
||||
"created": 1734387424,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o3-mini",
|
||||
"object": "model",
|
||||
"created": 1737146383,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o3-mini-2025-01-31",
|
||||
"object": "model",
|
||||
"created": 1738010200,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-2024-11-20",
|
||||
"object": "model",
|
||||
"created": 1739331543,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-search-preview-2025-03-11",
|
||||
"object": "model",
|
||||
"created": 1741388170,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-search-preview",
|
||||
"object": "model",
|
||||
"created": 1741388720,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini-search-preview-2025-03-11",
|
||||
"object": "model",
|
||||
"created": 1741390858,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini-search-preview",
|
||||
"object": "model",
|
||||
"created": 1741391161,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-transcribe",
|
||||
"object": "model",
|
||||
"created": 1742068463,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini-transcribe",
|
||||
"object": "model",
|
||||
"created": 1742068596,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o1-pro-2025-03-19",
|
||||
"object": "model",
|
||||
"created": 1742251504,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o1-pro",
|
||||
"object": "model",
|
||||
"created": 1742251791,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini-tts",
|
||||
"object": "model",
|
||||
"created": 1742403959,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o3-2025-04-16",
|
||||
"object": "model",
|
||||
"created": 1744133301,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o4-mini-2025-04-16",
|
||||
"object": "model",
|
||||
"created": 1744133506,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o3",
|
||||
"object": "model",
|
||||
"created": 1744225308,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o4-mini",
|
||||
"object": "model",
|
||||
"created": 1744225351,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4.1-2025-04-14",
|
||||
"object": "model",
|
||||
"created": 1744315746,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4.1",
|
||||
"object": "model",
|
||||
"created": 1744316542,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4.1-mini-2025-04-14",
|
||||
"object": "model",
|
||||
"created": 1744317547,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4.1-mini",
|
||||
"object": "model",
|
||||
"created": 1744318173,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4.1-nano-2025-04-14",
|
||||
"object": "model",
|
||||
"created": 1744321025,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4.1-nano",
|
||||
"object": "model",
|
||||
"created": 1744321707,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-image-1",
|
||||
"object": "model",
|
||||
"created": 1745517030,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "codex-mini-latest",
|
||||
"object": "model",
|
||||
"created": 1746673257,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o3-pro",
|
||||
"object": "model",
|
||||
"created": 1748475349,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-realtime-preview-2025-06-03",
|
||||
"object": "model",
|
||||
"created": 1748907838,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-audio-preview-2025-06-03",
|
||||
"object": "model",
|
||||
"created": 1748908498,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o3-pro-2025-06-10",
|
||||
"object": "model",
|
||||
"created": 1749166761,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o4-mini-deep-research",
|
||||
"object": "model",
|
||||
"created": 1749685485,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o3-deep-research",
|
||||
"object": "model",
|
||||
"created": 1749840121,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o3-deep-research-2025-06-26",
|
||||
"object": "model",
|
||||
"created": 1750865219,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "o4-mini-deep-research-2025-06-26",
|
||||
"object": "model",
|
||||
"created": 1750866121,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-5-chat-latest",
|
||||
"object": "model",
|
||||
"created": 1754073306,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-5-2025-08-07",
|
||||
"object": "model",
|
||||
"created": 1754075360,
|
||||
"owned_by": "system"
|
||||
},
|
||||
{
|
||||
"id": "gpt-3.5-turbo-16k",
|
||||
"object": "model",
|
||||
"created": 1683758102,
|
||||
"owned_by": "openai-internal"
|
||||
},
|
||||
{
|
||||
"id": "tts-1",
|
||||
"object": "model",
|
||||
"created": 1681940951,
|
||||
"owned_by": "openai-internal"
|
||||
},
|
||||
{
|
||||
"id": "whisper-1",
|
||||
"object": "model",
|
||||
"created": 1677532384,
|
||||
"owned_by": "openai-internal"
|
||||
},
|
||||
{
|
||||
"id": "text-embedding-ada-002",
|
||||
"object": "model",
|
||||
"created": 1671217299,
|
||||
"owned_by": "openai-internal"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
Simple request + error which occurs before streaming begins (where applicable).
|
||||
|
||||
-- request --
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "how many angels can dance on the head of a pin\n"
|
||||
}
|
||||
],
|
||||
"model": "gpt-4.1",
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
HTTP/2.0 400 Bad Request
|
||||
Content-Length: 281
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"error": {
|
||||
"message": "Input tokens exceed the configured limit of 272000 tokens. Your messages resulted in 3148588 tokens. Please reduce the length of the messages.",
|
||||
"type": "invalid_request_error",
|
||||
"param": "messages",
|
||||
"code": "context_length_exceeded"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-- non-streaming --
|
||||
HTTP/2.0 400 Bad Request
|
||||
Content-Length: 281
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"error": {
|
||||
"message": "Input tokens exceed the configured limit of 272000 tokens. Your messages resulted in 3148588 tokens. Please reduce the length of the messages.",
|
||||
"type": "invalid_request_error",
|
||||
"param": "messages",
|
||||
"code": "context_length_exceeded"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,536 @@
|
||||
Simple request.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "how many angels can dance on the head of a pin\n"
|
||||
}
|
||||
],
|
||||
"model": "gpt-4.1"
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" question"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"How"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" many"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" angels"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" can"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" dance"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" on"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" head"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" pin"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"?\""},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" classic"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" example"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" **"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"ph"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"ilos"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"oph"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"ical"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" theological"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" r"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"iddle"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"**,"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" not"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" genuine"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" inquiry"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" about"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" metaph"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"ysical"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" realities"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" The"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" phrase"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" most"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" likely"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" originated"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" during"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" **"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"med"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"ieval"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" schol"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"astic"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" debates"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"**,"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" where"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" scholars"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" engaged"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" complex"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" discussions"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" about"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" nature"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" spiritual"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" beings"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" limits"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" human"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" knowledge"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":".\n\n"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"###"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" Meaning"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" Context"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" **"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"Not"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" meant"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" have"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" literal"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" answer"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":":**"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" Angels"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" Christian"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" theology"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" are"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" spiritual"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" ("},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"not"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" physical"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":")"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" beings"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" so"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" they"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" don"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"’t"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" occupy"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" space"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" physical"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" sense"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":".\n"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" **"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"Symbol"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"ic"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" purpose"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":":**"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" The"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" question"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" often"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" used"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" mock"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" illustrate"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" arguments"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" perceived"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" as"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" overly"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" speculative"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" irrelevant"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":".\n\n"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"###"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"Answers"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"\""},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" through"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" History"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" **"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"Sch"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"ol"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"ast"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"ics"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":":**"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" There's"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" little"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" evidence"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" medieval"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" scholars"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" literally"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" debated"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" this"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":";"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" it's"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" more"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" later"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" **"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"car"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"ic"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"ature"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"**"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" their"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" intricate"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" theological"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" arguments"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":".\n"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" **"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"Modern"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" usage"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":":**"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" It's"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" cited"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" as"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" an"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" example"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" pointless"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" un"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"answer"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"able"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" question"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":".\n\n"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"###"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" Summary"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"**"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"There"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" no"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" specific"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" number"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":";"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"**"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" question"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" rhetorical"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" highlighting"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" limits"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" theoretical"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" speculative"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" reasoning"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":".\n\n"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"Would"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" you"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" like"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" know"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" more"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" about"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" medieval"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" schol"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"astic"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" debates"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" or"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" how"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" this"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" question"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" used"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" in"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" modern"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" discourse"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"?"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[],"usage":{"prompt_tokens":19,"completion_tokens":238,"total_tokens":257,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N",
|
||||
"object": "chat.completion",
|
||||
"created": 1753357765,
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "The question \"How many angels can dance on the head of a pin?\" is a classic example of a rhetorical or philosophical question—*not* a real theological inquiry.\n\n**Origin and Meaning:**\n- The phrase is used to lampoon or satirize overly subtle, speculative, or irrelevant philosophical debates, especially those attributed to medieval scholasticism.\n- There is **no actual historical record** of medieval theologians debating this specific question.\n- It **illustrates debates about the nature of angels**—whether they occupy physical space, for example—but not in such literal terms.\n\n**If answered literally:**\n- If angels are considered non-corporeal and not limited by physical space, **an infinite number** could \"dance\" on the head of a pin.\n- If taken as a joke, the answer is up to the storyteller!\n\n**In summary:** \nIt's a facetious question highlighting the limits or absurdities of some philosophical or theological arguments. There is no fixed answer.",
|
||||
"refusal": null,
|
||||
"annotations": []
|
||||
},
|
||||
"logprobs": null,
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 19,
|
||||
"completion_tokens": 200,
|
||||
"total_tokens": 219,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_b3f1157249"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
LLM (https://llm.datasette.io/) configured with a simple "read_file" tool.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "how large is the README.md file in my current path"
|
||||
}
|
||||
],
|
||||
"model": "gpt-4.1",
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "read_file",
|
||||
"description": "Read the contents of a file at the given path.",
|
||||
"parameters": {
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
data: {"id":"chatcmpl-BwkwXxA0yAyLKZelloERJWtxKor9z","object":"chat.completion.chunk","created":1753343173,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_b3f1157249","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_HjeqP7YeRkoNj0de9e3U4X4B","type":"function","function":{"name":"read_file","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwkwXxA0yAyLKZelloERJWtxKor9z","object":"chat.completion.chunk","created":1753343173,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_b3f1157249","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwkwXxA0yAyLKZelloERJWtxKor9z","object":"chat.completion.chunk","created":1753343173,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_b3f1157249","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"path"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwkwXxA0yAyLKZelloERJWtxKor9z","object":"chat.completion.chunk","created":1753343173,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_b3f1157249","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwkwXxA0yAyLKZelloERJWtxKor9z","object":"chat.completion.chunk","created":1753343173,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_b3f1157249","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"README"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwkwXxA0yAyLKZelloERJWtxKor9z","object":"chat.completion.chunk","created":1753343173,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_b3f1157249","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".md"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwkwXxA0yAyLKZelloERJWtxKor9z","object":"chat.completion.chunk","created":1753343173,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_b3f1157249","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwkwXxA0yAyLKZelloERJWtxKor9z","object":"chat.completion.chunk","created":1753343173,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_b3f1157249","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwkwXxA0yAyLKZelloERJWtxKor9z","object":"chat.completion.chunk","created":1753343173,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_b3f1157249","choices":[],"usage":{"prompt_tokens":60,"completion_tokens":15,"total_tokens":75,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "chatcmpl-BwkyFElDIr1egmFyfQ9z4vPBto7m2",
|
||||
"object": "chat.completion",
|
||||
"created": 1753343279,
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": null,
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_KjzAbhiZC6nk81tQzL7pwlpc",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "read_file",
|
||||
"arguments": "{\"path\":\"README.md\"}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"refusal": null,
|
||||
"annotations": []
|
||||
},
|
||||
"logprobs": null,
|
||||
"finish_reason": "tool_calls"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 60,
|
||||
"completion_tokens": 15,
|
||||
"total_tokens": 75,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_b3f1157249"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
Coder MCP tools automatically injected.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"model": "gpt-4.1",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "list coder workspace IDs for admin"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ha7QSWuIrCLSg"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"I"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"TxlRNztDyni152"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" am"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"d8rQaibDQpyL"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" about"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Qlbfp6UEp"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"68rb1Vo3ymBh"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" call"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"i7c6mc6zJY"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Z9syl1x73E7"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" appropriate"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"5wK"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" tool"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"qxf0biXh4i"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"UMXRLeWr9r7g"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" list"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"PkO0yHjNu3"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" all"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ktUBR7vT2FC"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" work"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"xdNr1gCRJW"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"spaces"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"5z5luvhUz"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"G6D7Ze3OlLR"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"6BZ54FOiuA7"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" user"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"6b0xOBQj2J"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" admin"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"X5gzNDQyO"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"oSONGErPa7g"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" display"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"EK9oGdN"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" their"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"TPtBmjMIt"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" IDs"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"FONB73iSePd"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":".\n\n"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"VMpWnam5jp"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_0TxntkwDB66KH8z4RwNqeWrZ","type":"function","function":{"name":"bmcp_coder_coder_list_workspaces","arguments":""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"kY"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"n5"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"owner"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"admin"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":""}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"1t"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null,"obfuscation":"sDj"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTooFaxeQgtyLB1kg53t41aB0NV","object":"chat.completion.chunk","created":1754479216,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[],"usage":{"prompt_tokens":4862,"completion_tokens":45,"total_tokens":4907,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"8sIWE1chOW"}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
|
||||
-- streaming/tool-call --
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"DBu9uyty0Uhux"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"Here"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Pk0tDwr0wkd"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" are"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ACu9WW1Lsz4"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"xrXWRUKKAZl"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" workspace"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"LowCw"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" IDs"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"RXNpYewll1k"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" for"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"WnyxJrani1M"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"JrnDAJOLap4"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" user"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"RNZIdDo4vj"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" admin"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"nJ7O0qcsG"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":":\n\n"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"0k0UVPjnE2"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"dtGIleZ8Nl9lU7"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" Workspace"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"wKNWu"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" Name"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"cmzvcWMEIp"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"GsImQO12UCnPHY"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" bob"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"AR4Jvn87StW"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"WoNeyT7BKKjIS"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"2Ou4DytumVPlyW"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" Workspace"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"PRWw3"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" ID"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"rrKKjluNdVET"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"v6NUOTV1Pd6piU"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" dd"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"UuYGjaLT7OXO"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"711"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"vLHjJVhbJgec"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"d"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"2yDtuCir4L9eyS"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"5"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"kyJOHcdfo1NMrP"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"c"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"nuKRieC0bpf6O3"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"q29JHHRnNg1GYt"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"83"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"e0o7Zu6eKnter"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"c"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"NCASF3SYR9GDQl"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"6"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"eG48V9XgxodtbB"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"CpP8ALTDfT0yBv"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"4"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"uQY85IhRAfuFl9"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"c"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"wsdJSv3bN65S5a"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"08"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"dq2JARx8gsgIm"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"-a"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"4booyOM91IZdC"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"0"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"wVJJDjNFBXO3OC"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"af"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"XFtDbXdnHdnF3"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"-b"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"juymtEmZxo1Ez"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"730"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"8pIOLoJZJAfe"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"559"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"NPfQJmrtGPlY"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"06"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"jsqxOojcWTY3A"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"e"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"cWYFwWie0ciIju"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"8"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ilVWzWQLUWQOMw"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"c"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ea99MtCCypPar2"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"\n\n"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"SDq7UD3LcH7"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"Let"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"S343Ji05lUgD"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" me"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"TTCD9vPg98sO"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" know"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"xcsP3lRI6f"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" if"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"bS0qh0vq73n3"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" you"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"pxUYdxCHoy8"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" need"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"wjLDXO4uD8"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" more"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"B6ckyharjv"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" information"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"xrN"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" about"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"aqv4RrWxJ"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" any"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"hqdG5QSND4E"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"HvfgjMOXU6aG"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" these"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"yE0jSPMkD"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":" work"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"wWfGxJR2wt"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"spaces"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"hOXndth8X"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"MReMwESHIpaDyo"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"EFeFvdS8m"}
|
||||
|
||||
data: {"id":"chatcmpl-C1WTqhYgK7bV01bW98Lww3zqaf8ZF","object":"chat.completion.chunk","created":1754479218,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_799e4ca3f1","choices":[],"usage":{"prompt_tokens":5049,"completion_tokens":60,"total_tokens":5109,"prompt_tokens_details":{"cached_tokens":4864,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"0JQt7Fw"}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "chatcmpl-C1XAKDTVYnmWS7tgvg7vPje00PIiy",
|
||||
"object": "chat.completion",
|
||||
"created": 1754481852,
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "I am about to call the relevant function to list all workspaces for the user admin and provide their workspace IDs.\n\nExecuting the function call now.",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_aEuQAWKQYInC6fQ4z0iatdVP",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "bmcp_coder_coder_list_workspaces",
|
||||
"arguments": "{\"owner\":\"admin\"}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"refusal": null,
|
||||
"annotations": []
|
||||
},
|
||||
"logprobs": null,
|
||||
"finish_reason": "tool_calls"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 4862,
|
||||
"completion_tokens": 45,
|
||||
"total_tokens": 4914,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 0,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_51e1070cf2"
|
||||
}
|
||||
|
||||
|
||||
-- non-streaming/tool-call --
|
||||
{
|
||||
"id": "chatcmpl-C1XANLwdflVxAjKOjbMP3LJxSlXsS",
|
||||
"object": "chat.completion",
|
||||
"created": 1754481855,
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Here is the list of Coder workspace IDs for the user admin:\n\n- Workspace Name: bob\n- Workspace ID: dd711d5c-83c6-4c08-a0af-b73055906e8c\n\nLet me know if you need more details or actions on this workspace!",
|
||||
"refusal": null,
|
||||
"annotations": []
|
||||
},
|
||||
"logprobs": null,
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 5049,
|
||||
"completion_tokens": 60,
|
||||
"total_tokens": 5119,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 4864,
|
||||
"audio_tokens": 0
|
||||
},
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 0,
|
||||
"audio_tokens": 0,
|
||||
"accepted_prediction_tokens": 0,
|
||||
"rejected_prediction_tokens": 0
|
||||
}
|
||||
},
|
||||
"service_tier": "default",
|
||||
"system_fingerprint": "fp_51e1070cf2"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
Simple request + error.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "how many angels can dance on the head of a pin\n"
|
||||
}
|
||||
],
|
||||
"model": "gpt-4.1",
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" question"},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"id":"chatcmpl-BwoiPTGRbKkY5rncfaM0s9KtWrq5N","object":"chat.completion.chunk","created":1753357673,"model":"gpt-4.1-2025-04-14","service_tier":"default","system_fingerprint":"fp_51e1070cf2","choices":[{"index":0,"delta":{"content":" \""},"logprobs":null,"finish_reason":null}],"usage":null}
|
||||
|
||||
data: {"error": {"message": "The server had an error while processing your request. Sorry about that!", "type": "server_error"}}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
Streaming response where the provider returns an injected tool call as the first chunk with no text preamble.
|
||||
This test ensures tool invocation continues even when no chunks are relayed to the client.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"content": "<current_datetime>2026-01-22T18:35:17.612Z</current_datetime>\n\nlist all my coder workspaces",
|
||||
"role": "user"
|
||||
}
|
||||
],
|
||||
"model": "claude-haiku-4.5",
|
||||
"n": 1,
|
||||
"temperature": 1,
|
||||
"parallel_tool_calls": false,
|
||||
"stream_options": {
|
||||
"include_usage": true
|
||||
},
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"name":"bmcp_coder_coder_list_workspaces"},"id":"toolu_vrtx_01CvBi1d4qpKTG2PCuc9wDbZ","index":0,"type":"function"}]}}],"created":1769106921,"id":"msg_vrtx_01UoiRJwj3JXcwNYAh3z7ARs","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":""},"index":0}]}}],"created":1769106921,"id":"msg_vrtx_01UoiRJwj3JXcwNYAh3z7ARs","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":"{\"own"},"index":0}]}}],"created":1769106921,"id":"msg_vrtx_01UoiRJwj3JXcwNYAh3z7ARs","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":"er\": \"me\"}"},"index":0}]}}],"created":1769106921,"id":"msg_vrtx_01UoiRJwj3JXcwNYAh3z7ARs","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"finish_reason":"tool_calls","index":0,"delta":{"content":null}}],"created":1769106921,"id":"msg_vrtx_01UoiRJwj3JXcwNYAh3z7ARs","usage":{"completion_tokens":65,"prompt_tokens":25716,"prompt_tokens_details":{"cached_tokens":20470},"total_tokens":25781},"model":"claude-haiku-4.5"}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
|
||||
-- streaming/tool-call --
|
||||
data: {"choices":[{"index":0,"delta":{"content":"You","role":"assistant"}}],"created":1769198061,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" have one","role":"assistant"}}],"created":1769198061,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" Coder workspace:","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"\n\n**test-scf** (","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"ID: a174a2e5","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"-5050-445d-89","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"ff-dd720e5b442","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"e)\n- Template: docker","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"\n- Template Version","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" ID","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":": ad1b5ab1-","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"fc18-4792-84f","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"7-797787607d30","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"\n- Status","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":": Up","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" to date","role":"assistant"}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"finish_reason":"stop","index":0,"delta":{"content":null}}],"created":1769198062,"id":"msg_vrtx_015B1npskreQgEjMrfsdjH1m","usage":{"completion_tokens":85,"prompt_tokens":25989,"prompt_tokens_details":{"cached_tokens":0},"total_tokens":26074},"model":"claude-haiku-4.5"}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
Streaming response where the provider returns text content followed by an injected tool call at index 1 (instead of index 0).
|
||||
This can happen when the provider incorrectly continues indexing from a previous response.
|
||||
This tests that nil entries are removed from the tool calls array caused by non-zero starting indices.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"content": "<current_datetime>2026-01-23T20:22:43.781Z</current_datetime>\n\nI want you to do to this in order:\n1) create a file in my current directory with name \"test.txt\"\n2) list all my coder workspaces",
|
||||
"role": "user"
|
||||
}
|
||||
],
|
||||
"model": "claude-haiku-4.5",
|
||||
"n": 1,
|
||||
"temperature": 1,
|
||||
"parallel_tool_calls": false,
|
||||
"stream_options": {
|
||||
"include_usage": true
|
||||
},
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
data: {"choices":[{"index":0,"delta":{"content":"Now","role":"assistant"}}],"created":1769199774,"id":"msg_vrtx_01Fiieb5Z3kqJf9a3FwvLkky","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" listing","role":"assistant"}}],"created":1769199774,"id":"msg_vrtx_01Fiieb5Z3kqJf9a3FwvLkky","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" your","role":"assistant"}}],"created":1769199774,"id":"msg_vrtx_01Fiieb5Z3kqJf9a3FwvLkky","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" C","role":"assistant"}}],"created":1769199774,"id":"msg_vrtx_01Fiieb5Z3kqJf9a3FwvLkky","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"oder workspaces:","role":"assistant"}}],"created":1769199774,"id":"msg_vrtx_01Fiieb5Z3kqJf9a3FwvLkky","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"name":"bmcp_coder_coder_list_workspaces"},"id":"toolu_vrtx_01DbFqUgk6aAtJ4nDBqzFWDF","index":1,"type":"function"}]}}],"created":1769199774,"id":"msg_vrtx_01Fiieb5Z3kqJf9a3FwvLkky","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":null,"tool_calls":[{"function":{"arguments":""},"index":1}]}}],"created":1769199774,"id":"msg_vrtx_01Fiieb5Z3kqJf9a3FwvLkky","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"finish_reason":"tool_calls","index":0,"delta":{"content":null}}],"created":1769199774,"id":"msg_vrtx_01Fiieb5Z3kqJf9a3FwvLkky","usage":{"completion_tokens":58,"prompt_tokens":25939,"prompt_tokens_details":{"cached_tokens":25429},"total_tokens":25997},"model":"claude-haiku-4.5"}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
|
||||
-- streaming/tool-call --
|
||||
data: {"choices":[{"index":0,"delta":{"content":"Done","role":"assistant"}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"! I create","role":"assistant"}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"d `","role":"assistant"}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"test.txt` in","role":"assistant"}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" your current directory.","role":"assistant"}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" You","role":"assistant"}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" have","role":"assistant"}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" 1","role":"assistant"}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" ","role":"assistant"}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":"Coder workspace:\n\n-","role":"assistant"}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" **test-scf** (docker","role":"assistant"}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"index":0,"delta":{"content":" template)","role":"assistant"}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","model":"claude-haiku-4.5"}
|
||||
|
||||
data: {"choices":[{"finish_reason":"stop","index":0,"delta":{"content":null}}],"created":1769199776,"id":"msg_vrtx_01RVxamMyw1DBtpoENDpmnQK","usage":{"completion_tokens":39,"prompt_tokens":26166,"prompt_tokens_details":{"cached_tokens":25934},"total_tokens":26205},"model":"claude-haiku-4.5"}
|
||||
|
||||
data: [DONE]
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "This was a large input...",
|
||||
"model": "gpt-4.1",
|
||||
"prompt_cache_key": "key-123",
|
||||
"prompt_cache_retention": "24h",
|
||||
"stream": false
|
||||
}
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "resp_0cd5d6b8310055d600696a1776b42c81a199fbb02248a8bfa0",
|
||||
"object": "response",
|
||||
"created_at": 1768560502,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"completed_at": 1768560504,
|
||||
"error": null,
|
||||
"frequency_penalty": 0.0,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_0cd5d6b8310055d600696a177708b881a1bb53034def764104",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "- I provide clear, accurate, and concise answers tailored to your requests.\n- I can process and summarize large volumes of information quickly.\n- I adapt my responses based on your needs and instructions for precision and relevance."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"presence_penalty": 0.0,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": "key-123",
|
||||
"prompt_cache_retention": "24h",
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 12033,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 11904
|
||||
},
|
||||
"output_tokens": 44,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 12077
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
-- request --
|
||||
{
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Is 3 + 5 a prime number? Use the add function to calculate the sum."
|
||||
}
|
||||
],
|
||||
"model": "gpt-5.4",
|
||||
"stream": false,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "add",
|
||||
"description": "Add two numbers together.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "resp_0aba2ac43dc240b30169b15720243c819ebb64977365d42cf5",
|
||||
"object": "response",
|
||||
"created_at": 1773229856,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"completed_at": 1773229861,
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-5.4-2026-03-05",
|
||||
"output": [
|
||||
{
|
||||
"id": "rs_0aba2ac43dc240b30169b157208c88819e8238a91b5f7a919b",
|
||||
"type": "reasoning",
|
||||
"status": "completed",
|
||||
"encrypted_content": "gAAAAA==",
|
||||
"summary": []
|
||||
},
|
||||
{
|
||||
"id": "msg_0aba2ac43dc240b30169b1572286d0819eb24b1d0f84c8fb3f",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"text": "Checking whether 3 + 5 is prime by calling the add function first."
|
||||
}
|
||||
],
|
||||
"phase": "commentary",
|
||||
"role": "assistant"
|
||||
},
|
||||
{
|
||||
"id": "fc_0aba2ac43dc240b30169b157255604819e8a108124efc1635c",
|
||||
"type": "function_call",
|
||||
"status": "completed",
|
||||
"arguments": "{\"a\":3,\"b\":5}",
|
||||
"call_id": "call_A8TkZmIcKtw2Zw952Wc5QVe7",
|
||||
"name": "add"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"prompt_cache_retention": null,
|
||||
"reasoning": {
|
||||
"effort": "xhigh",
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": false,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "low"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"description": "Add two numbers together.",
|
||||
"name": "add",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true
|
||||
}
|
||||
],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 0.98,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 58,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 30,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 10
|
||||
},
|
||||
"total_tokens": 88
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
-- request --
|
||||
{
|
||||
"conversation": "conv_695fa15ecbb881958e89ac2d35d918ed0c9f1f0524a858fa",
|
||||
"input": "explain why this is funny.",
|
||||
"model": "gpt-4o-mini",
|
||||
"stream": false
|
||||
}
|
||||
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "resp_0c9f1f0524a858fa00695fa15fc5a081958f4304aafd3bdec2",
|
||||
"object": "response",
|
||||
"created_at": 1767874911,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"completed_at": 1767874914,
|
||||
"conversation": {
|
||||
"id": "conv_695fa15ecbb881958e89ac2d35d918ed0c9f1f0524a858fa"
|
||||
},
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_0c9f1f0524a858fa00695fa1605bd48195b65b4dfd732941bc",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "This joke plays on a double meaning of the phrase \u201cmake up.\u201d \n\n1. **Literal Meaning**: Atoms are the basic building blocks of matter and literally \"make up\" all substances in the universe.\n\n2. **Figurative Meaning**: The phrase \"make up\" can also mean to fabricate or lie about something. \n\nThe humor comes from the unexpected twist; it starts off sounding like a serious statement about atoms, then surprises us with a clever play on words that suggests atoms are dishonest. This blend of scientific fact and pun creates the comedic effect!"
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"prompt_cache_retention": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 48,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 116,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 164
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "Use the code_exec tool to print hello world to the console.",
|
||||
"model": "gpt-5",
|
||||
"tools": [
|
||||
{
|
||||
"type": "custom",
|
||||
"name": "code_exec",
|
||||
"description": "Executes arbitrary Python code."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "resp_09c614364030cdf000696942589da081a0af07f5859acb7308",
|
||||
"object": "response",
|
||||
"created_at": 1768505944,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"completed_at": 1768505948,
|
||||
"error": null,
|
||||
"frequency_penalty": 0.0,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-5-2025-08-07",
|
||||
"output": [
|
||||
{
|
||||
"id": "rs_09c614364030cdf00069694258e45881a0b8d5f198cde47d58",
|
||||
"type": "reasoning",
|
||||
"summary": []
|
||||
},
|
||||
{
|
||||
"id": "ctc_09c614364030cdf0006969425bf33481a09cc0f9522af2d980",
|
||||
"type": "custom_tool_call",
|
||||
"status": "completed",
|
||||
"call_id": "call_haf8njtwrVZ1754Gm6fjAtuA",
|
||||
"input": "print(\"hello world\")",
|
||||
"name": "code_exec"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"presence_penalty": 0.0,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"prompt_cache_retention": null,
|
||||
"reasoning": {
|
||||
"effort": "medium",
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [
|
||||
{
|
||||
"type": "custom",
|
||||
"description": "Executes arbitrary Python code.",
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"name": "code_exec"
|
||||
}
|
||||
],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 64,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 148,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 128
|
||||
},
|
||||
"total_tokens": 212
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "tell me a joke",
|
||||
"model": "gpt-4o-mini",
|
||||
"stream": false
|
||||
}
|
||||
|
||||
-- non-streaming --
|
||||
HTTP/2.0 401 Unauthorized
|
||||
Content-Length: 234
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"error": {
|
||||
"message": "Incorrect API key provided: sk-***. You can find your API key at https://platform.openai.com/account/api-keys.",
|
||||
"type": "authentication_error",
|
||||
"param": null,
|
||||
"code": "invalid_api_key"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
Two reasoning output items before a function_call.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Is 3 + 5 a prime number? Use the add function to calculate the sum."
|
||||
}
|
||||
],
|
||||
"model": "gpt-4.1",
|
||||
"stream": false,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "add",
|
||||
"description": "Add two numbers together.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "resp_0da6045a8b68fa5200695fa23dcc2c81a19c849f627abf8a31",
|
||||
"object": "response",
|
||||
"created_at": 1767875133,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"completed_at": 1767875134,
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"output": [
|
||||
{
|
||||
"id": "rs_0da6045a8b68fa5200695fa23e100081a19bf68887d47ae93d",
|
||||
"type": "reasoning",
|
||||
"status": "completed",
|
||||
"summary": [
|
||||
{
|
||||
"type": "summary_text",
|
||||
"text": "The user wants to add 3 and 5. Let me call the add function."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rs_1aa7045a8b68fa5200695fa23e200082b29cf79998e58bf94e",
|
||||
"type": "reasoning",
|
||||
"status": "completed",
|
||||
"summary": [
|
||||
{
|
||||
"type": "summary_text",
|
||||
"text": "After adding, I will check if the result is prime."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "fc_0da6045a8b68fa5200695fa23e198081a19bf68887d47ae93d",
|
||||
"type": "function_call",
|
||||
"status": "completed",
|
||||
"arguments": "{\"a\":3,\"b\":5}",
|
||||
"call_id": "call_CJSaa2u51JG996575oVljuNq",
|
||||
"name": "add"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"prompt_cache_retention": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"description": "Add two numbers together.",
|
||||
"name": "add",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true
|
||||
}
|
||||
],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 58,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 18,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 76
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "explain why this is funny.",
|
||||
"model": "gpt-4o-mini",
|
||||
"previous_response_id": "resp_0388c79043df3e3400695f9f83cd6481959062cec6830d8d51",
|
||||
"stream": false
|
||||
}
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "resp_0388c79043df3e3400695f9f86cfa08195af1f015c60117a83",
|
||||
"object": "response",
|
||||
"created_at": 1767874438,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"completed_at": 1767874441,
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_0388c79043df3e3400695f9f87369c8195a0d1a82a06f96d56",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "The joke plays on a clever wordplay and a double meaning. \n\n1. **Outstanding in his field**: The phrase can mean that someone is exceptionally good at what they do (outstanding performance) and also literally refers to the scarecrow being in a field (like a farm field). \n\n2. **Scarecrow context**: Scarecrows are placed in fields to scare away birds, so the idea of a scarecrow being \"outstanding\" can lead to a funny mental image.\n\nThe humor comes from the unexpected twist of a literal phrase being interpreted in a figurative way, creating a light and playful pun."
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": "resp_0388c79043df3e3400695f9f83cd6481959062cec6830d8d51",
|
||||
"prompt_cache_key": null,
|
||||
"prompt_cache_retention": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 43,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 129,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 172
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "tell me a joke",
|
||||
"model": "gpt-4o-mini",
|
||||
"stream": false
|
||||
}
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "resp_0388c79043df3e3400695f9f83cd6481959062cec6830d8d51",
|
||||
"object": "response",
|
||||
"created_at": 1767874435,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"completed_at": 1767874436,
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_0388c79043df3e3400695f9f8447a08195af2ef951966823c4",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "Why did the scarecrow win an award?\n\nBecause he was outstanding in his field!"
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"prompt_cache_retention": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 11,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 18,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 29
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
-- request --
|
||||
{
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Is 3 + 5 a prime number? Use the add function to calculate the sum."
|
||||
}
|
||||
],
|
||||
"model": "gpt-4.1",
|
||||
"stream": false,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "add",
|
||||
"description": "Add two numbers together.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "resp_0da6045a8b68fa5200695fa23dcc2c81a19c849f627abf8a31",
|
||||
"object": "response",
|
||||
"created_at": 1767875133,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"completed_at": 1767875134,
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"output": [
|
||||
{
|
||||
"id": "rs_0da6045a8b68fa5200695fa23e100081a19bf68887d47ae93d",
|
||||
"type": "reasoning",
|
||||
"status": "completed",
|
||||
"summary": [
|
||||
{
|
||||
"type": "summary_text",
|
||||
"text": "The user wants to add 3 and 5. Let me call the add function."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "fc_0da6045a8b68fa5200695fa23e198081a19bf68887d47ae93d",
|
||||
"type": "function_call",
|
||||
"status": "completed",
|
||||
"arguments": "{\"a\":3,\"b\":5}",
|
||||
"call_id": "call_CJSaa2u51JG996575oVljuNq",
|
||||
"name": "add"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"prompt_cache_retention": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"description": "Add two numbers together.",
|
||||
"name": "add",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true
|
||||
}
|
||||
],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 58,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 18,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 76
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
-- request --
|
||||
{
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Is 3 + 5 a prime number? Also add 10 + 20. Use the add function for both."
|
||||
}
|
||||
],
|
||||
"model": "gpt-4.1",
|
||||
"stream": false,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "add",
|
||||
"description": "Add two numbers together.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "resp_parallel_blocking_001",
|
||||
"object": "response",
|
||||
"created_at": 1767875133,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"completed_at": 1767875134,
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4.1-2025-04-14",
|
||||
"output": [
|
||||
{
|
||||
"id": "rs_parallel_blocking_reasoning_001",
|
||||
"type": "reasoning",
|
||||
"status": "completed",
|
||||
"summary": [
|
||||
{
|
||||
"type": "summary_text",
|
||||
"text": "The user wants two additions: 3+5 and 10+20. I'll call add for both."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "fc_parallel_blocking_first_001",
|
||||
"type": "function_call",
|
||||
"status": "completed",
|
||||
"arguments": "{\"a\":3,\"b\":5}",
|
||||
"call_id": "call_ParallelBlockingFirst001",
|
||||
"name": "add"
|
||||
},
|
||||
{
|
||||
"id": "fc_parallel_blocking_second_001",
|
||||
"type": "function_call",
|
||||
"status": "completed",
|
||||
"arguments": "{\"a\":10,\"b\":20}",
|
||||
"call_id": "call_ParallelBlockingSecond01",
|
||||
"name": "add"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"prompt_cache_retention": null,
|
||||
"reasoning": {
|
||||
"effort": null,
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "medium"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"description": "Add two numbers together.",
|
||||
"name": "add",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true
|
||||
}
|
||||
],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 65,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 30,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 0
|
||||
},
|
||||
"total_tokens": 95
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+146
@@ -0,0 +1,146 @@
|
||||
Both a reasoning summary and a commentary message before a function_call.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Is 3 + 5 a prime number? Use the add function to calculate the sum."
|
||||
}
|
||||
],
|
||||
"model": "gpt-5.4",
|
||||
"stream": false,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "add",
|
||||
"description": "Add two numbers together.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "resp_1bba3bc54ed351c41270c26831354d920fcc75088476e53de6",
|
||||
"object": "response",
|
||||
"created_at": 1773229900,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"completed_at": 1773229905,
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-5.4-2026-03-05",
|
||||
"output": [
|
||||
{
|
||||
"id": "rs_1bba3bc54ed351c41270c26831908d920fcc75088476e53de6",
|
||||
"type": "reasoning",
|
||||
"status": "completed",
|
||||
"encrypted_content": "gAAAAA==",
|
||||
"summary": [
|
||||
{
|
||||
"type": "summary_text",
|
||||
"text": "I need to add 3 and 5 to check primality."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "msg_1bba3bc54ed351c41270c26831a09d920fdd86199587f64ef7",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"text": "Let me calculate the sum first using the add function."
|
||||
}
|
||||
],
|
||||
"phase": "commentary",
|
||||
"role": "assistant"
|
||||
},
|
||||
{
|
||||
"id": "fc_1bba3bc54ed351c41270c26831b0ad920fee97200698074f08",
|
||||
"type": "function_call",
|
||||
"status": "completed",
|
||||
"arguments": "{\"a\":3,\"b\":5}",
|
||||
"call_id": "call_B9UjYX01Lvvv1XwjDsdmRW3f",
|
||||
"name": "add"
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"prompt_cache_key": null,
|
||||
"prompt_cache_retention": null,
|
||||
"reasoning": {
|
||||
"effort": "xhigh",
|
||||
"summary": null
|
||||
},
|
||||
"safety_identifier": null,
|
||||
"service_tier": "default",
|
||||
"store": false,
|
||||
"temperature": 1.0,
|
||||
"text": {
|
||||
"format": {
|
||||
"type": "text"
|
||||
},
|
||||
"verbosity": "low"
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"description": "Add two numbers together.",
|
||||
"name": "add",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"strict": true
|
||||
}
|
||||
],
|
||||
"top_logprobs": 0,
|
||||
"top_p": 0.98,
|
||||
"truncation": "disabled",
|
||||
"usage": {
|
||||
"input_tokens": 58,
|
||||
"input_tokens_details": {
|
||||
"cached_tokens": 0
|
||||
},
|
||||
"output_tokens": 35,
|
||||
"output_tokens_details": {
|
||||
"reasoning_tokens": 10
|
||||
},
|
||||
"total_tokens": 93
|
||||
},
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "hello",
|
||||
"model": "gpt-6.7"
|
||||
}
|
||||
|
||||
-- non-streaming --
|
||||
{
|
||||
"id": "resp_0388c79043df3e3400695f9f83cd6481959062cec6830d8d51",
|
||||
"object": "response",
|
||||
"created_at": 1767874435,
|
||||
"status": "completed",
|
||||
"background": false,
|
||||
"billing": {
|
||||
"payer": "developer"
|
||||
},
|
||||
"completed_at": 1767874436,
|
||||
"error": null,
|
||||
"incomplete_details": null,
|
||||
"instructions": null,
|
||||
"max_output_tokens": null,
|
||||
"max_tool_calls": null,
|
||||
"model": "gpt-4o-mini-2024-07-18",
|
||||
"output": [
|
||||
{
|
||||
"id": "msg_0388c79043df3e3400695f9f8447a08195af2ef951966823c4",
|
||||
"type": "message",
|
||||
"status": "completed",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"annotations": [],
|
||||
"logprobs": [],
|
||||
"text": "This json is formatted wrong"
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
],
|
||||
@@ -0,0 +1,98 @@
|
||||
-- request --
|
||||
{
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Is 3 + 5 a prime number? Use the add function to calculate the sum."
|
||||
}
|
||||
],
|
||||
"model": "gpt-4.1",
|
||||
"stream": true,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "add",
|
||||
"description": "Add two numbers together.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_0c3fb28cfcf463a500695fa2f0239481a095ec6ce3dfe4d458","object":"response","created_at":1767875312,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_0c3fb28cfcf463a500695fa2f0239481a095ec6ce3dfe4d458","object":"response","created_at":1767875312,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","type":"reasoning","status":"in_progress","summary":[]},"output_index":0,"sequence_number":2}
|
||||
|
||||
event: response.reasoning_summary_part.added
|
||||
data: {"type":"response.reasoning_summary_part.added","item_id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","output_index":0,"part":{"type":"summary_text","text":""},"summary_index":0,"sequence_number":3}
|
||||
|
||||
event: response.reasoning_summary_text.delta
|
||||
data: {"type":"response.reasoning_summary_text.delta","item_id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","output_index":0,"summary_index":0,"delta":"The user wants to add 3 and 5. Let me call the add function.","sequence_number":4}
|
||||
|
||||
event: response.reasoning_summary_text.done
|
||||
data: {"type":"response.reasoning_summary_text.done","item_id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","output_index":0,"summary_index":0,"text":"The user wants to add 3 and 5. Let me call the add function.","sequence_number":5}
|
||||
|
||||
event: response.reasoning_summary_part.done
|
||||
data: {"type":"response.reasoning_summary_part.done","item_id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","output_index":0,"part":{"type":"summary_text","text":"The user wants to add 3 and 5. Let me call the add function."},"summary_index":0,"sequence_number":6}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","type":"reasoning","status":"completed","summary":[{"type":"summary_text","text":"The user wants to add 3 and 5. Let me call the add function."}]},"output_index":0,"sequence_number":7}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","type":"function_call","status":"in_progress","arguments":"","call_id":"call_7VaiUXZYuuuwWwviCrckxq6t","name":"add"},"output_index":1,"sequence_number":8}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"{\"","item_id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","obfuscation":"gWZHP8i4lSgQYT","output_index":1,"sequence_number":9}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"a","item_id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","obfuscation":"yC1iubuqc098ZSH","output_index":1,"sequence_number":10}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"\":","item_id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","obfuscation":"G17nNbWUcJkqA2","output_index":1,"sequence_number":11}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"3","item_id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","obfuscation":"Mj71L4eeLZbIEFU","output_index":1,"sequence_number":12}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":",\"","item_id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","obfuscation":"ZchcCauvlPtVc7","output_index":1,"sequence_number":13}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"b","item_id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","obfuscation":"gWLYMrsBI3ZHKVP","output_index":1,"sequence_number":14}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"\":","item_id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","obfuscation":"n4iUzpnbPE4DnO","output_index":1,"sequence_number":15}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"5","item_id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","obfuscation":"23mO3rxkXqDOi6g","output_index":1,"sequence_number":16}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"}","item_id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","obfuscation":"AQnBsNz7GqkdylH","output_index":1,"sequence_number":17}
|
||||
|
||||
event: response.function_call_arguments.done
|
||||
data: {"type":"response.function_call_arguments.done","arguments":"{\"a\":3,\"b\":5}","item_id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","output_index":1,"sequence_number":18}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","type":"function_call","status":"completed","arguments":"{\"a\":3,\"b\":5}","call_id":"call_7VaiUXZYuuuwWwviCrckxq6t","name":"add"},"output_index":1,"sequence_number":19}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","response":{"id":"resp_0c3fb28cfcf463a500695fa2f0239481a095ec6ce3dfe4d458","object":"response","created_at":1767875312,"status":"completed","background":false,"completed_at":1767875312,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","output":[{"id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","type":"reasoning","status":"completed","summary":[{"type":"summary_text","text":"The user wants to add 3 and 5. Let me call the add function."}]},{"id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","type":"function_call","status":"completed","arguments":"{\"a\":3,\"b\":5}","call_id":"call_7VaiUXZYuuuwWwviCrckxq6t","name":"add"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":58,"input_tokens_details":{"cached_tokens":0},"output_tokens":18,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":76},"user":null,"metadata":{}},"sequence_number":20}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
-- request --
|
||||
{
|
||||
"model": "gpt-5.2-codex",
|
||||
"input": "Test cached input tokens.",
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_05080461b406f3f501696a1409d34c8195a40ff4b092145c35","object":"response","created_at":1768559625,"status":"in_progress","background":false,"completed_at":null,"error":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.2-codex","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":"detailed"},"service_tier":"auto","store":false,"temperature":1.0,"tool_choice":"auto","tools":[],"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_05080461b406f3f501696a1409d34c8195a40ff4b092145c35","object":"response","created_at":1768559625,"status":"in_progress","background":false,"completed_at":null,"error":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.2-codex","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":"detailed"},"service_tier":"auto","store":false,"temperature":1.0,"tool_choice":"auto","tools":[],"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"msg_05080461b406f3f501696a140a70d88195a2ce4c1a4eb39696","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":0,"sequence_number":2}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_05080461b406f3f501696a140a70d88195a2ce4c1a4eb39696","output_index":0,"part":{"type":"output_text","annotations":[],"text":""},"sequence_number":3}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"Test","item_id":"msg_05080461b406f3f501696a140a70d88195a2ce4c1a4eb39696","output_index":0,"sequence_number":4}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" response","item_id":"msg_05080461b406f3f501696a140a70d88195a2ce4c1a4eb39696","output_index":0,"sequence_number":5}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" with","item_id":"msg_05080461b406f3f501696a140a70d88195a2ce4c1a4eb39696","output_index":0,"sequence_number":6}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" cached","item_id":"msg_05080461b406f3f501696a140a70d88195a2ce4c1a4eb39696","output_index":0,"sequence_number":7}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" tokens.","item_id":"msg_05080461b406f3f501696a140a70d88195a2ce4c1a4eb39696","output_index":0,"sequence_number":8}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_05080461b406f3f501696a140a70d88195a2ce4c1a4eb39696","output_index":0,"text":"Test response with cached tokens.","sequence_number":9}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_05080461b406f3f501696a140a70d88195a2ce4c1a4eb39696","output_index":0,"part":{"type":"output_text","annotations":[],"text":"Test response with cached tokens."},"sequence_number":10}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"msg_05080461b406f3f501696a140a70d88195a2ce4c1a4eb39696","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"text":"Test response with cached tokens."}],"role":"assistant"},"output_index":0,"sequence_number":11}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","response":{"id":"resp_05080461b406f3f501696a1409d34c8195a40ff4b092145c35","object":"response","created_at":1768559625,"status":"completed","background":false,"completed_at":1768559627,"error":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.2-codex","output":[{"id":"msg_05080461b406f3f501696a140a70d88195a2ce4c1a4eb39696","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"text":"Test response with cached tokens."}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":"019bc657-f77b-7292-b5f4-2e8d6c2b0945","prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":"detailed"},"service_tier":"default","store":false,"temperature":1.0,"tool_choice":"auto","tools":[],"truncation":"disabled","usage":{"input_tokens":16909,"input_tokens_details":{"cached_tokens":15744},"output_tokens":54,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":16963},"user":null,"metadata":{}},"sequence_number":12}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,80 @@
|
||||
-- request --
|
||||
{
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Is 3 + 5 a prime number? Use the add function to calculate the sum."
|
||||
}
|
||||
],
|
||||
"model": "gpt-5.4",
|
||||
"stream": true,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "add",
|
||||
"description": "Add two numbers together.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_0aba2ac43dc240b30169b15720243c819ebb64977365d42cf5","object":"response","created_at":1773229856,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.4-2026-03-05","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"xhigh","summary":null},"safety_identifier":null,"service_tier":"default","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"low"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":0.98,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_0aba2ac43dc240b30169b15720243c819ebb64977365d42cf5","object":"response","created_at":1773229856,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.4-2026-03-05","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"xhigh","summary":null},"safety_identifier":null,"service_tier":"default","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"low"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":0.98,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"rs_0aba2ac43dc240b30169b157208c88819e8238a91b5f7a919b","type":"reasoning","status":"in_progress","summary":[]},"output_index":0,"sequence_number":2}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"rs_0aba2ac43dc240b30169b157208c88819e8238a91b5f7a919b","type":"reasoning","status":"completed","encrypted_content":"gAAAAA==","summary":[]},"output_index":0,"sequence_number":3}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"msg_0aba2ac43dc240b30169b1572286d0819eb24b1d0f84c8fb3f","type":"message","status":"in_progress","content":[],"phase":"commentary","role":"assistant"},"output_index":1,"sequence_number":4}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","item_id":"msg_0aba2ac43dc240b30169b1572286d0819eb24b1d0f84c8fb3f","output_index":1,"content_index":0,"part":{"type":"output_text","text":"","annotations":[]},"sequence_number":5}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","item_id":"msg_0aba2ac43dc240b30169b1572286d0819eb24b1d0f84c8fb3f","output_index":1,"content_index":0,"delta":"Checking whether 3 + 5 is prime by calling the add function first.","sequence_number":6}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","item_id":"msg_0aba2ac43dc240b30169b1572286d0819eb24b1d0f84c8fb3f","output_index":1,"content_index":0,"text":"Checking whether 3 + 5 is prime by calling the add function first.","sequence_number":7}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","item_id":"msg_0aba2ac43dc240b30169b1572286d0819eb24b1d0f84c8fb3f","output_index":1,"content_index":0,"part":{"type":"output_text","text":"Checking whether 3 + 5 is prime by calling the add function first.","annotations":[]},"sequence_number":8}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"msg_0aba2ac43dc240b30169b1572286d0819eb24b1d0f84c8fb3f","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"text":"Checking whether 3 + 5 is prime by calling the add function first."}],"phase":"commentary","role":"assistant"},"output_index":1,"sequence_number":9}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"fc_0aba2ac43dc240b30169b157255604819e8a108124efc1635c","type":"function_call","status":"in_progress","arguments":"","call_id":"call_A8TkZmIcKtw2Zw952Wc5QVe7","name":"add"},"output_index":2,"sequence_number":10}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"{\"a\":3,\"b\":5}","item_id":"fc_0aba2ac43dc240b30169b157255604819e8a108124efc1635c","output_index":2,"sequence_number":11}
|
||||
|
||||
event: response.function_call_arguments.done
|
||||
data: {"type":"response.function_call_arguments.done","arguments":"{\"a\":3,\"b\":5}","item_id":"fc_0aba2ac43dc240b30169b157255604819e8a108124efc1635c","output_index":2,"sequence_number":12}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"fc_0aba2ac43dc240b30169b157255604819e8a108124efc1635c","type":"function_call","status":"completed","arguments":"{\"a\":3,\"b\":5}","call_id":"call_A8TkZmIcKtw2Zw952Wc5QVe7","name":"add"},"output_index":2,"sequence_number":13}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","response":{"id":"resp_0aba2ac43dc240b30169b15720243c819ebb64977365d42cf5","object":"response","created_at":1773229856,"status":"completed","background":false,"completed_at":1773229861,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.4-2026-03-05","output":[{"id":"rs_0aba2ac43dc240b30169b157208c88819e8238a91b5f7a919b","type":"reasoning","status":"completed","encrypted_content":"gAAAAA==","summary":[]},{"id":"msg_0aba2ac43dc240b30169b1572286d0819eb24b1d0f84c8fb3f","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"text":"Checking whether 3 + 5 is prime by calling the add function first."}],"phase":"commentary","role":"assistant"},{"id":"fc_0aba2ac43dc240b30169b157255604819e8a108124efc1635c","type":"function_call","status":"completed","arguments":"{\"a\":3,\"b\":5}","call_id":"call_A8TkZmIcKtw2Zw952Wc5QVe7","name":"add"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"xhigh","summary":null},"safety_identifier":null,"service_tier":"default","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"low"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":0.98,"truncation":"disabled","usage":{"input_tokens":58,"input_tokens_details":{"cached_tokens":0},"output_tokens":30,"output_tokens_details":{"reasoning_tokens":10},"total_tokens":88},"user":null,"metadata":{}},"sequence_number":14}
|
||||
|
||||
@@ -0,0 +1,540 @@
|
||||
-- request --
|
||||
{
|
||||
"conversation": "conv_695fa1132770819795d013275c77e8380108ce40c6fb22bd",
|
||||
"input": "explain why this is funny.",
|
||||
"model": "gpt-4o-mini",
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_0108ce40c6fb22bd00695fa11395588197a8207c74e6e3795c","object":"response","created_at":1767874835,"status":"in_progress","background":false,"completed_at":null,"conversation":{"id":"conv_695fa1132770819795d013275c77e8380108ce40c6fb22bd"},"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_0108ce40c6fb22bd00695fa11395588197a8207c74e6e3795c","object":"response","created_at":1767874835,"status":"in_progress","background":false,"completed_at":null,"conversation":{"id":"conv_695fa1132770819795d013275c77e8380108ce40c6fb22bd"},"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":0,"sequence_number":2}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""},"sequence_number":3}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"This","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"6JuS91EMbhLA","output_index":0,"sequence_number":4}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" joke","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"y4aKJq6ioqK","output_index":0,"sequence_number":5}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" is","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"OSK1qGQlQ45Gf","output_index":0,"sequence_number":6}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" funny","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"xOx3biYzfi","output_index":0,"sequence_number":7}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" for","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"B6nzgMtFCPfI","output_index":0,"sequence_number":8}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" a","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"NLJ3uuUUR7HEwL","output_index":0,"sequence_number":9}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" couple","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"axMyCq7cc","output_index":0,"sequence_number":10}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" of","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"wogQAHGbERhyj","output_index":0,"sequence_number":11}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" reasons","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"kaIWALH5","output_index":0,"sequence_number":12}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":":\n\n","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"5aWCXnTSm1Ww0","output_index":0,"sequence_number":13}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"1","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"ulbeCHj60aqERM2","output_index":0,"sequence_number":14}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"LS6N4ccoGtkBMf9","output_index":0,"sequence_number":15}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" **","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"RyhciW9kcGtT3","output_index":0,"sequence_number":16}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"Word","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"JJOH0y2lt5ce","output_index":0,"sequence_number":17}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"play","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"FweyacD1kgKU","output_index":0,"sequence_number":18}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"**","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"99utx5f2PR410S","output_index":0,"sequence_number":19}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":":","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"dZe5PeQsygjpDJU","output_index":0,"sequence_number":20}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" The","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"3UfyKaxhlu5T","output_index":0,"sequence_number":21}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" humor","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"aTNqJJdtlA","output_index":0,"sequence_number":22}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" comes","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"xK3buVbUHt","output_index":0,"sequence_number":23}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" from","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"igWwXO0tQtm","output_index":0,"sequence_number":24}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"A39bwmGkGF3T","output_index":0,"sequence_number":25}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" double","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"nLeuH3WdF","output_index":0,"sequence_number":26}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" meaning","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"zxC0qSSE","output_index":0,"sequence_number":27}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" of","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"DIMKV7wc7lnEa","output_index":0,"sequence_number":28}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"CnM6idZlt3Su","output_index":0,"sequence_number":29}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" phrase","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"DSxcKiYE2","output_index":0,"sequence_number":30}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" \"","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"zKE75xC70J5I8n","output_index":0,"sequence_number":31}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"make","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"oBFujacYh6Qi","output_index":0,"sequence_number":32}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" up","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"MCWKA9PGFz3uH","output_index":0,"sequence_number":33}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".\"","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"Mww11OYYfx46Pn","output_index":0,"sequence_number":34}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" In","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"lDHppT2E9fBjL","output_index":0,"sequence_number":35}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" one","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"qH7241nKwTjN","output_index":0,"sequence_number":36}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" sense","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"aQcSSHwJ3p","output_index":0,"sequence_number":37}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"ZNoviZFdXYechTT","output_index":0,"sequence_number":38}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" atoms","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"nXkzWnQfut","output_index":0,"sequence_number":39}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" are","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"9IE6b6ePg9E6","output_index":0,"sequence_number":40}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"MN8puLH01K4r","output_index":0,"sequence_number":41}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" basic","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"cHHGWtl6sA","output_index":0,"sequence_number":42}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" building","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"Qh8Lgl6","output_index":0,"sequence_number":43}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" blocks","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"usrQ4Zqhy","output_index":0,"sequence_number":44}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" of","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"UlMkWTr0buDdu","output_index":0,"sequence_number":45}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" matter","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"di7aKyqOB","output_index":0,"sequence_number":46}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" and","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"Jz1ouMsSH5Sq","output_index":0,"sequence_number":47}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" literally","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"bcPU64","output_index":0,"sequence_number":48}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" \"","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"k0mzekJTeeeyjl","output_index":0,"sequence_number":49}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"make","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"osOddu5z1SKn","output_index":0,"sequence_number":50}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" up","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"hxVor1fqBr85z","output_index":0,"sequence_number":51}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"\"","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"R6QtJIz32R1BVio","output_index":0,"sequence_number":52}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" everything","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"AwhOH","output_index":0,"sequence_number":53}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" in","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"OumZOuQTLGWst","output_index":0,"sequence_number":54}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"aJI4Tm9Si3rt","output_index":0,"sequence_number":55}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" physical","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"F1cKqO8","output_index":0,"sequence_number":56}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" world","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"QNMNuZEBTi","output_index":0,"sequence_number":57}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"MXn5ZYICLy6vCbY","output_index":0,"sequence_number":58}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" In","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"NeupGqbEKerw6","output_index":0,"sequence_number":59}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" another","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"K8tdy7U8","output_index":0,"sequence_number":60}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" sense","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"pjhD3Np58X","output_index":0,"sequence_number":61}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"ACou7OILpf3wWDR","output_index":0,"sequence_number":62}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" \"","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"L4nsA8ZF0swWRP","output_index":0,"sequence_number":63}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"making","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"loHLh0D52x","output_index":0,"sequence_number":64}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" up","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"ZCbUNkX3fmHK5","output_index":0,"sequence_number":65}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"\"","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"B9vFmLYXf6C0spM","output_index":0,"sequence_number":66}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" something","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"qYs53A","output_index":0,"sequence_number":67}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" can","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"zZfzpKfcLO4h","output_index":0,"sequence_number":68}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" mean","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"iEoAbAAy5dQ","output_index":0,"sequence_number":69}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" invent","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"ELQYNFOF4","output_index":0,"sequence_number":70}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"ing","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"c9S0EIus0bjBk","output_index":0,"sequence_number":71}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" or","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"zFOwG7sjVX8cZ","output_index":0,"sequence_number":72}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" lying","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"kLOSno5hAZ","output_index":0,"sequence_number":73}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" about","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"sZW682cjzl","output_index":0,"sequence_number":74}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" it","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"5SdVpOpP3tDW9","output_index":0,"sequence_number":75}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".\n\n","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"jIJkdpLZee7yv","output_index":0,"sequence_number":76}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"2","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"nPIBCntK2ClgdQs","output_index":0,"sequence_number":77}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"BzMXERtY6UTcark","output_index":0,"sequence_number":78}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" **","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"Gk753o2HBcSud","output_index":0,"sequence_number":79}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"Sur","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"UCUX6DSgEibpa","output_index":0,"sequence_number":80}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"prise","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"P9oQNuV01zl","output_index":0,"sequence_number":81}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" Element","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"qBups9bc","output_index":0,"sequence_number":82}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"**","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"Z9dIdjqTsefoUa","output_index":0,"sequence_number":83}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":":","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"qm08Sch66EBWq9k","output_index":0,"sequence_number":84}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" J","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"J9bucKcls8A7M6","output_index":0,"sequence_number":85}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"okes","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"waZa21wHngIb","output_index":0,"sequence_number":86}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" often","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"VFnDaAMga6","output_index":0,"sequence_number":87}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" rely","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"YAFlPgnPcJC","output_index":0,"sequence_number":88}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" on","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"lLGSFHXK52aiW","output_index":0,"sequence_number":89}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" a","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"T7x2svQFyo3BjR","output_index":0,"sequence_number":90}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" setup","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"ZMt6PMeCWr","output_index":0,"sequence_number":91}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" that","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"8l1qJa3KTEX","output_index":0,"sequence_number":92}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" leads","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"zhhqrWIZAm","output_index":0,"sequence_number":93}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"yWdpvincjoJy","output_index":0,"sequence_number":94}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" audience","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"0ozlgo3","output_index":0,"sequence_number":95}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" to","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"S1HPNJAwEcewT","output_index":0,"sequence_number":96}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" expect","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"8KjGDm8mT","output_index":0,"sequence_number":97}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" one","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"XXmBZEjiFMNK","output_index":0,"sequence_number":98}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" thing","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"zmoaWMkdXD","output_index":0,"sequence_number":99}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"HJoNcrcVeIKLodt","output_index":0,"sequence_number":100}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" only","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"fCI023RmwwQ","output_index":0,"sequence_number":101}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" to","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"2Zsh2cdqDmHB8","output_index":0,"sequence_number":102}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" deliver","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"Hu5TXO23","output_index":0,"sequence_number":103}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" an","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"VZuZDgkAFfI1d","output_index":0,"sequence_number":104}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" unexpected","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"XZdrj","output_index":0,"sequence_number":105}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" punch","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"YwFnYN01eH","output_index":0,"sequence_number":106}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"line","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"iR5aKzuGEseR","output_index":0,"sequence_number":107}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"kSY2QLPXpQKhhD7","output_index":0,"sequence_number":108}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" Here","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"3r3xEOpBXyF","output_index":0,"sequence_number":109}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"F69vhN3jEtN497d","output_index":0,"sequence_number":110}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"dySiTv3oGlxo","output_index":0,"sequence_number":111}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" punch","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"NCRSrY6Eb5","output_index":0,"sequence_number":112}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"line","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"cY6NHRaYJHx0","output_index":0,"sequence_number":113}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" plays","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"VPEZBBm0Hh","output_index":0,"sequence_number":114}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" with","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"eF3lZXVH1To","output_index":0,"sequence_number":115}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" our","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"GZ348T5reB6D","output_index":0,"sequence_number":116}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" understanding","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"j6","output_index":0,"sequence_number":117}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" of","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"PavNXetPHc38s","output_index":0,"sequence_number":118}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" language","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"Wj2Mv0J","output_index":0,"sequence_number":119}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"mWAw8s19WeQnY6i","output_index":0,"sequence_number":120}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" catching","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"3jyf8Cc","output_index":0,"sequence_number":121}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"J0L0wwVuGgxF","output_index":0,"sequence_number":122}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" listener","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"S2Vnlgk","output_index":0,"sequence_number":123}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" off","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"NtUUpay2a64F","output_index":0,"sequence_number":124}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" guard","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"b0wp7OyGDX","output_index":0,"sequence_number":125}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".\n\n","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"YKTvffawS9ptn","output_index":0,"sequence_number":126}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"3","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"NzNDjdBJrz4ag81","output_index":0,"sequence_number":127}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"rjI3dk1wGFtYDBd","output_index":0,"sequence_number":128}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" **","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"8WnxSsuSFODHO","output_index":0,"sequence_number":129}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"Rel","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"BhV12AQZ9qmT2","output_index":0,"sequence_number":130}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"atable","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"UTzXf0v3oH","output_index":0,"sequence_number":131}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" Knowledge","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"qZOZIo","output_index":0,"sequence_number":132}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"**","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"cJm6vlGXwyzZXy","output_index":0,"sequence_number":133}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":":","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"dNoUfruWzSEiGbh","output_index":0,"sequence_number":134}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" The","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"9biJGwkcf8DT","output_index":0,"sequence_number":135}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" joke","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"Fc2ayZORxSk","output_index":0,"sequence_number":136}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" uses","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"I2yi0U5MA3a","output_index":0,"sequence_number":137}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" common","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"0u1MaStc6","output_index":0,"sequence_number":138}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" knowledge","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"IRlavB","output_index":0,"sequence_number":139}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" about","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"CbPPGMmDGP","output_index":0,"sequence_number":140}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" science","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"s5Vc9kMd","output_index":0,"sequence_number":141}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" (","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"4aUXFyZztDOb20","output_index":0,"sequence_number":142}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"atoms","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"DwBfSdw5Z3T","output_index":0,"sequence_number":143}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":")","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"gdKE9yfh3BfiOk8","output_index":0,"sequence_number":144}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" in","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"lcnGy3TQDzeBy","output_index":0,"sequence_number":145}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" a","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"7sx3DNuKWmMa7t","output_index":0,"sequence_number":146}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" light","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"6LZkpgf4xU","output_index":0,"sequence_number":147}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"hearted","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"AvS1EEdHW","output_index":0,"sequence_number":148}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" way","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"h0NWSBAWvBOV","output_index":0,"sequence_number":149}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"Cbi5mDUOpI44h46","output_index":0,"sequence_number":150}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" allowing","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"715Tb92","output_index":0,"sequence_number":151}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" it","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"Yg9uD6tBhUwFO","output_index":0,"sequence_number":152}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" to","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"tNVbx8ZDFQ8SY","output_index":0,"sequence_number":153}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" resonate","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"gUJhGv2","output_index":0,"sequence_number":154}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" with","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"AgivlEZAqmk","output_index":0,"sequence_number":155}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" a","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"lXG5SHj7QhLL1s","output_index":0,"sequence_number":156}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" wide","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"b0BP9ORJI2X","output_index":0,"sequence_number":157}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" audience","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"zMj6fOG","output_index":0,"sequence_number":158}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".\n\n","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"Agq84NjYCn4xs","output_index":0,"sequence_number":159}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"These","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"dof54LQG7uE","output_index":0,"sequence_number":160}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" elements","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"1oWvGIK","output_index":0,"sequence_number":161}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" combine","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"kvuq0yp6","output_index":0,"sequence_number":162}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" to","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"SEn7dk277XYB5","output_index":0,"sequence_number":163}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" create","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"hyGSspNs9","output_index":0,"sequence_number":164}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" a","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"cO1mGkek487Zem","output_index":0,"sequence_number":165}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" playful","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"kJJQB4N6","output_index":0,"sequence_number":166}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" twist","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"CTJ0Ri1sOS","output_index":0,"sequence_number":167}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" that","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"xFCmJyq5ghR","output_index":0,"sequence_number":168}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" el","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"INwzSkCCOVkWg","output_index":0,"sequence_number":169}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"icits","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"9rgQQMWSwBj","output_index":0,"sequence_number":170}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" laughter","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"ymfcFY8","output_index":0,"sequence_number":171}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"!","item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"obfuscation":"QOWTZahcZGIHoZB","output_index":0,"sequence_number":172}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","logprobs":[],"output_index":0,"sequence_number":173,"text":"This joke is funny for a couple of reasons:\n\n1. **Wordplay**: The humor comes from the double meaning of the phrase \"make up.\" In one sense, atoms are the basic building blocks of matter and literally \"make up\" everything in the physical world. In another sense, \"making up\" something can mean inventing or lying about it.\n\n2. **Surprise Element**: Jokes often rely on a setup that leads the audience to expect one thing, only to deliver an unexpected punchline. Here, the punchline plays with our understanding of language, catching the listener off guard.\n\n3. **Relatable Knowledge**: The joke uses common knowledge about science (atoms) in a lighthearted way, allowing it to resonate with a wide audience.\n\nThese elements combine to create a playful twist that elicits laughter!"}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"This joke is funny for a couple of reasons:\n\n1. **Wordplay**: The humor comes from the double meaning of the phrase \"make up.\" In one sense, atoms are the basic building blocks of matter and literally \"make up\" everything in the physical world. In another sense, \"making up\" something can mean inventing or lying about it.\n\n2. **Surprise Element**: Jokes often rely on a setup that leads the audience to expect one thing, only to deliver an unexpected punchline. Here, the punchline plays with our understanding of language, catching the listener off guard.\n\n3. **Relatable Knowledge**: The joke uses common knowledge about science (atoms) in a lighthearted way, allowing it to resonate with a wide audience.\n\nThese elements combine to create a playful twist that elicits laughter!"},"sequence_number":174}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"msg_0108ce40c6fb22bd00695fa11416548197bd5b43b5a507d23d","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"This joke is funny for a couple of reasons:\n\n1. **Wordplay**: The humor comes from the double meaning of the phrase \"make up.\" In one sense, atoms are the basic building blocks of matter and literally \"make up\" everything in the physical world. In another sense, \"making up\" something can mean inventing or lying about it.\n\n2. **Surprise Element**: Jokes often rely on a setup that leads the audience to expect one thing, only to deliver an unexpected punchline. Here, the punchline plays with our understanding of language, catching the listener off guard.\n\n3. **Relatable Knowledge**: The joke uses common knowledge about science (atoms) in a lighthearted way, allowing it to resonate with a wide audience.\n\nThese elements combine to create a playful twist that elicits laughter!"}],"role":"assistant"},"output_index":0,"sequence_number":175}
|
||||
|
||||
event: error
|
||||
data: {"type":"error","error":{"type":"invalid_request_error","code":null,"message":"Conversation with id 'conv_695fa1132770819795d013275c77e8380108ce40c6fb22bd' not found.","param":null},"sequence_number":177}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "Use the code_exec tool to print hello world to the console.",
|
||||
"model": "gpt-5",
|
||||
"stream": true,
|
||||
"tools": [
|
||||
{
|
||||
"type": "custom",
|
||||
"name": "code_exec",
|
||||
"description": "Executes arbitrary Python code."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_0c26996bc41c2a0500696942e83634819fb71b2b8ff8a4a76c","object":"response","created_at":1768506088,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5-2025-08-07","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"custom","description":"Executes arbitrary Python code.","format":{"type":"text"},"name":"code_exec"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_0c26996bc41c2a0500696942e83634819fb71b2b8ff8a4a76c","object":"response","created_at":1768506088,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5-2025-08-07","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"custom","description":"Executes arbitrary Python code.","format":{"type":"text"},"name":"code_exec"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"rs_0c26996bc41c2a0500696942e8ae90819fb421c1b6a945aa99","type":"reasoning","summary":[]},"output_index":0,"sequence_number":2}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"rs_0c26996bc41c2a0500696942e8ae90819fb421c1b6a945aa99","type":"reasoning","summary":[]},"output_index":0,"sequence_number":3}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"ctc_0c26996bc41c2a0500696942ee6db8819fa6e841317eecbfb2","type":"custom_tool_call","status":"in_progress","call_id":"call_2gSnF58IEhXLwlbnqbm5XKMd","input":"","name":"code_exec"},"output_index":1,"sequence_number":4}
|
||||
|
||||
event: response.custom_tool_call_input.delta
|
||||
data: {"type":"response.custom_tool_call_input.delta","delta":"print","item_id":"ctc_0c26996bc41c2a0500696942ee6db8819fa6e841317eecbfb2","obfuscation":"sTDUEAHu5aJ","output_index":1,"sequence_number":5}
|
||||
|
||||
event: response.custom_tool_call_input.delta
|
||||
data: {"type":"response.custom_tool_call_input.delta","delta":"(\"","item_id":"ctc_0c26996bc41c2a0500696942ee6db8819fa6e841317eecbfb2","obfuscation":"qvFA5MbN9ZUnBH","output_index":1,"sequence_number":6}
|
||||
|
||||
event: response.custom_tool_call_input.delta
|
||||
data: {"type":"response.custom_tool_call_input.delta","delta":"hello","item_id":"ctc_0c26996bc41c2a0500696942ee6db8819fa6e841317eecbfb2","obfuscation":"rRrXgQDOuwG","output_index":1,"sequence_number":7}
|
||||
|
||||
event: response.custom_tool_call_input.delta
|
||||
data: {"type":"response.custom_tool_call_input.delta","delta":" world","item_id":"ctc_0c26996bc41c2a0500696942ee6db8819fa6e841317eecbfb2","obfuscation":"DwnJdEFXvZ","output_index":1,"sequence_number":8}
|
||||
|
||||
event: response.custom_tool_call_input.delta
|
||||
data: {"type":"response.custom_tool_call_input.delta","delta":"\")","item_id":"ctc_0c26996bc41c2a0500696942ee6db8819fa6e841317eecbfb2","obfuscation":"pEr2t8Vpv3Ij96","output_index":1,"sequence_number":9}
|
||||
|
||||
event: response.custom_tool_call_input.done
|
||||
data: {"type":"response.custom_tool_call_input.done","input":"print(\"hello world\")","item_id":"ctc_0c26996bc41c2a0500696942ee6db8819fa6e841317eecbfb2","output_index":1,"sequence_number":10}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"ctc_0c26996bc41c2a0500696942ee6db8819fa6e841317eecbfb2","type":"custom_tool_call","status":"completed","call_id":"call_2gSnF58IEhXLwlbnqbm5XKMd","input":"print(\"hello world\")","name":"code_exec"},"output_index":1,"sequence_number":11}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","response":{"id":"resp_0c26996bc41c2a0500696942e83634819fb71b2b8ff8a4a76c","object":"response","created_at":1768506088,"status":"completed","background":false,"completed_at":1768506095,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5-2025-08-07","output":[{"id":"rs_0c26996bc41c2a0500696942e8ae90819fb421c1b6a945aa99","type":"reasoning","summary":[]},{"id":"ctc_0c26996bc41c2a0500696942ee6db8819fa6e841317eecbfb2","type":"custom_tool_call","status":"completed","call_id":"call_2gSnF58IEhXLwlbnqbm5XKMd","input":"print(\"hello world\")","name":"code_exec"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"custom","description":"Executes arbitrary Python code.","format":{"type":"text"},"name":"code_exec"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":64,"input_tokens_details":{"cached_tokens":0},"output_tokens":340,"output_tokens_details":{"reasoning_tokens":320},"total_tokens":404},"user":null,"metadata":{}},"sequence_number":12}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "tell me a joke",
|
||||
"model": "gpt-4o-mini",
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
HTTP/2.0 429 Too Many Requests
|
||||
Content-Length: 176
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"error": {
|
||||
"message": "Rate limit exceeded. Please try again in 20 seconds.",
|
||||
"type": "rate_limit_error",
|
||||
"param": null,
|
||||
"code": "rate_limit_exceeded"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
Two reasoning output items before a function_call.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Is 3 + 5 a prime number? Use the add function to calculate the sum."
|
||||
}
|
||||
],
|
||||
"model": "gpt-4.1",
|
||||
"stream": true,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "add",
|
||||
"description": "Add two numbers together.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_0c3fb28cfcf463a500695fa2f0239481a095ec6ce3dfe4d458","object":"response","created_at":1767875312,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_0c3fb28cfcf463a500695fa2f0239481a095ec6ce3dfe4d458","object":"response","created_at":1767875312,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","type":"reasoning","status":"in_progress","summary":[]},"output_index":0,"sequence_number":2}
|
||||
|
||||
event: response.reasoning_summary_part.added
|
||||
data: {"type":"response.reasoning_summary_part.added","item_id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","output_index":0,"part":{"type":"summary_text","text":""},"summary_index":0,"sequence_number":3}
|
||||
|
||||
event: response.reasoning_summary_text.delta
|
||||
data: {"type":"response.reasoning_summary_text.delta","item_id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","output_index":0,"summary_index":0,"delta":"The user wants to add 3 and 5. Let me call the add function.","sequence_number":4}
|
||||
|
||||
event: response.reasoning_summary_text.done
|
||||
data: {"type":"response.reasoning_summary_text.done","item_id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","output_index":0,"summary_index":0,"text":"The user wants to add 3 and 5. Let me call the add function.","sequence_number":5}
|
||||
|
||||
event: response.reasoning_summary_part.done
|
||||
data: {"type":"response.reasoning_summary_part.done","item_id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","output_index":0,"part":{"type":"summary_text","text":"The user wants to add 3 and 5. Let me call the add function."},"summary_index":0,"sequence_number":6}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","type":"reasoning","status":"completed","summary":[{"type":"summary_text","text":"The user wants to add 3 and 5. Let me call the add function."}]},"output_index":0,"sequence_number":7}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"rs_1aa7045a8b68fa5200695fa23e200082b29cf79998e58bf94e","type":"reasoning","status":"in_progress","summary":[]},"output_index":1,"sequence_number":8}
|
||||
|
||||
event: response.reasoning_summary_part.added
|
||||
data: {"type":"response.reasoning_summary_part.added","item_id":"rs_1aa7045a8b68fa5200695fa23e200082b29cf79998e58bf94e","output_index":1,"part":{"type":"summary_text","text":""},"summary_index":0,"sequence_number":9}
|
||||
|
||||
event: response.reasoning_summary_text.delta
|
||||
data: {"type":"response.reasoning_summary_text.delta","item_id":"rs_1aa7045a8b68fa5200695fa23e200082b29cf79998e58bf94e","output_index":1,"summary_index":0,"delta":"After adding, I will check if the result is prime.","sequence_number":10}
|
||||
|
||||
event: response.reasoning_summary_text.done
|
||||
data: {"type":"response.reasoning_summary_text.done","item_id":"rs_1aa7045a8b68fa5200695fa23e200082b29cf79998e58bf94e","output_index":1,"summary_index":0,"text":"After adding, I will check if the result is prime.","sequence_number":11}
|
||||
|
||||
event: response.reasoning_summary_part.done
|
||||
data: {"type":"response.reasoning_summary_part.done","item_id":"rs_1aa7045a8b68fa5200695fa23e200082b29cf79998e58bf94e","output_index":1,"part":{"type":"summary_text","text":"After adding, I will check if the result is prime."},"summary_index":0,"sequence_number":12}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"rs_1aa7045a8b68fa5200695fa23e200082b29cf79998e58bf94e","type":"reasoning","status":"completed","summary":[{"type":"summary_text","text":"After adding, I will check if the result is prime."}]},"output_index":1,"sequence_number":13}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","type":"function_call","status":"in_progress","arguments":"","call_id":"call_7VaiUXZYuuuwWwviCrckxq6t","name":"add"},"output_index":2,"sequence_number":14}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"{\"a\":3,\"b\":5}","item_id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","obfuscation":"gWZHP8i4lSgQYT","output_index":2,"sequence_number":15}
|
||||
|
||||
event: response.function_call_arguments.done
|
||||
data: {"type":"response.function_call_arguments.done","arguments":"{\"a\":3,\"b\":5}","item_id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","output_index":2,"sequence_number":16}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","type":"function_call","status":"completed","arguments":"{\"a\":3,\"b\":5}","call_id":"call_7VaiUXZYuuuwWwviCrckxq6t","name":"add"},"output_index":2,"sequence_number":17}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","response":{"id":"resp_0c3fb28cfcf463a500695fa2f0239481a095ec6ce3dfe4d458","object":"response","created_at":1767875312,"status":"completed","background":false,"completed_at":1767875312,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","output":[{"id":"rs_0c3fb28cfcf463a500695fa2f0a0a881a0890103ba88b0628e","type":"reasoning","status":"completed","summary":[{"type":"summary_text","text":"The user wants to add 3 and 5. Let me call the add function."}]},{"id":"rs_1aa7045a8b68fa5200695fa23e200082b29cf79998e58bf94e","type":"reasoning","status":"completed","summary":[{"type":"summary_text","text":"After adding, I will check if the result is prime."}]},{"id":"fc_0c3fb28cfcf463a500695fa2f0b0a881a0890103ba88b0628e","type":"function_call","status":"completed","arguments":"{\"a\":3,\"b\":5}","call_id":"call_7VaiUXZYuuuwWwviCrckxq6t","name":"add"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":58,"input_tokens_details":{"cached_tokens":0},"output_tokens":18,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":76},"user":null,"metadata":{}},"sequence_number":18}
|
||||
|
||||
@@ -0,0 +1,576 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "explain why this is funny.",
|
||||
"model": "gpt-4o-mini",
|
||||
"previous_response_id": "resp_0f9c4b2f224d858000695fa062bf048197a680f357bbb09000",
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_0f9c4b2f224d858000695fa0649b8c8197b38914b15a7add0e","object":"response","created_at":1767874660,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":"resp_0f9c4b2f224d858000695fa062bf048197a680f357bbb09000","prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_0f9c4b2f224d858000695fa0649b8c8197b38914b15a7add0e","object":"response","created_at":1767874660,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":"resp_0f9c4b2f224d858000695fa062bf048197a680f357bbb09000","prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":0,"sequence_number":2}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""},"sequence_number":3}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"The","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"DHEzS6FGVUr5E","output_index":0,"sequence_number":4}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" joke","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"QHJlLKd1i4I","output_index":0,"sequence_number":5}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" is","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"OUQeCkINJ5VDR","output_index":0,"sequence_number":6}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" funny","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"edUq2nh7rM","output_index":0,"sequence_number":7}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" because","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"lfIvyMYF","output_index":0,"sequence_number":8}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" it","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"IevxLSVnUQUv1","output_index":0,"sequence_number":9}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" uses","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"WCP3pFvqO6f","output_index":0,"sequence_number":10}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" a","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"Q5qCDtvROr5ZP0","output_index":0,"sequence_number":11}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" play","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"uYCIUmPmOxY","output_index":0,"sequence_number":12}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" on","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"eDN8BZywTMbfE","output_index":0,"sequence_number":13}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" words","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"m9d5ApPbls","output_index":0,"sequence_number":14}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"tZo36JrN5e2844D","output_index":0,"sequence_number":15}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" which","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"CVRHFumykU","output_index":0,"sequence_number":16}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" is","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"rdAYifDkSO66w","output_index":0,"sequence_number":17}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" a","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"qdkX1IGsZFixdS","output_index":0,"sequence_number":18}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" common","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"wqcOXveYt","output_index":0,"sequence_number":19}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" form","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"TkeTQ4v6hWr","output_index":0,"sequence_number":20}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" of","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"D38VdvUE7l0H9","output_index":0,"sequence_number":21}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" humor","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"iGyDNUGr0C","output_index":0,"sequence_number":22}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"cutbtYnZfT0n4JO","output_index":0,"sequence_number":23}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" \n\n","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"AnxZS7kyw6A9j","output_index":0,"sequence_number":24}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"1","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"RzSDkMTUnlSn0MZ","output_index":0,"sequence_number":25}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"5QY6AzdMey52NAl","output_index":0,"sequence_number":26}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" **","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"IfJewJwbvV84B","output_index":0,"sequence_number":27}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"Double","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"d1QfJAfDG1","output_index":0,"sequence_number":28}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" Meaning","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"uUtusErd","output_index":0,"sequence_number":29}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"**","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"eEynq2ECHVNFHD","output_index":0,"sequence_number":30}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":":","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"KFnQwxpnVwbMrCS","output_index":0,"sequence_number":31}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" The","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"EmahvP8dVtog","output_index":0,"sequence_number":32}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" phrase","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"vWNyEuOHx","output_index":0,"sequence_number":33}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" \"","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"lAqrd6cYAXlhCz","output_index":0,"sequence_number":34}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"out","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"M2xl0znKS7ci1","output_index":0,"sequence_number":35}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"standing","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"e7X0kd8A","output_index":0,"sequence_number":36}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" in","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"ghB38DUHuwyZv","output_index":0,"sequence_number":37}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" his","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"T53kggqnrHeK","output_index":0,"sequence_number":38}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" field","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"jc98KS0TBP","output_index":0,"sequence_number":39}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"\"","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"vYewPc6Rn7twA59","output_index":0,"sequence_number":40}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" can","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"89reGpcrNM4F","output_index":0,"sequence_number":41}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" be","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"b5CoQSqeiPpDZ","output_index":0,"sequence_number":42}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" interpreted","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"K9js","output_index":0,"sequence_number":43}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" literally","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"weYNMB","output_index":0,"sequence_number":44}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"dkNP1549QnPgaK5","output_index":0,"sequence_number":45}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" meaning","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"smEFitne","output_index":0,"sequence_number":46}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"zKo3ymbuz2f3","output_index":0,"sequence_number":47}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" scare","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"3R7vsK0FsP","output_index":0,"sequence_number":48}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"crow","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"4f59ggc8KAOe","output_index":0,"sequence_number":49}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" is","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"c6MBXeF3KPdZ9","output_index":0,"sequence_number":50}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" literally","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"fMSP1r","output_index":0,"sequence_number":51}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" standing","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"ka1O1zO","output_index":0,"sequence_number":52}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" out","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"OxpPkKaOI4gI","output_index":0,"sequence_number":53}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" in","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"zKfYV5jEfCzt7","output_index":0,"sequence_number":54}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" a","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"KJg3i2F6LFQxzp","output_index":0,"sequence_number":55}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" field","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"HfFZ4RRe3f","output_index":0,"sequence_number":56}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" (","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"pQ4oXqVqV36gE0","output_index":0,"sequence_number":57}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"as","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"8SaeYXxOQU3cnd","output_index":0,"sequence_number":58}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" that's","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"MKgo8fAnG","output_index":0,"sequence_number":59}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" where","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"2fo6SoMB7u","output_index":0,"sequence_number":60}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" scare","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"HNfJHQO7Lu","output_index":0,"sequence_number":61}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"c","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"tJm1UVUt453MlZC","output_index":0,"sequence_number":62}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"rows","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"boBkPXPM6PM0","output_index":0,"sequence_number":63}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" are","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"4wv4vIp7bnqT","output_index":0,"sequence_number":64}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" found","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"7jbVDFFDrR","output_index":0,"sequence_number":65}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":").","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"iPVX4f8Nk2R36u","output_index":0,"sequence_number":66}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" However","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"WXD8NM59","output_index":0,"sequence_number":67}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"0zylfpXdumQWL3A","output_index":0,"sequence_number":68}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" it","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"r21NPwPwh6gWv","output_index":0,"sequence_number":69}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" also","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"yBuwgjQM3TS","output_index":0,"sequence_number":70}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" has","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"bKu6Uq5lPnBt","output_index":0,"sequence_number":71}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" a","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"UqLYVw32sivCxo","output_index":0,"sequence_number":72}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" figur","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"D9R8bxIy42","output_index":0,"sequence_number":73}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"ative","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"VPMseVGqlG2","output_index":0,"sequence_number":74}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" meaning","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"qKBa0orJ","output_index":0,"sequence_number":75}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":":","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"eXIpmNUtluw8Kvs","output_index":0,"sequence_number":76}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" it","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"1VBnyXJquHKL3","output_index":0,"sequence_number":77}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" suggests","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"b7tCjGH","output_index":0,"sequence_number":78}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" that","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"a0OorLr8zoQ","output_index":0,"sequence_number":79}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" someone","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"ihsOjyxt","output_index":0,"sequence_number":80}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" is","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"li0qLt2sYBmxJ","output_index":0,"sequence_number":81}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" exceptionally","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"FE","output_index":0,"sequence_number":82}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" skilled","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"v9HhHkN0","output_index":0,"sequence_number":83}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" or","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"mRkKQtBPBkrFb","output_index":0,"sequence_number":84}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" accomplished","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"cul","output_index":0,"sequence_number":85}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" in","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"3MJtuI4xfHA14","output_index":0,"sequence_number":86}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" their","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"rfRTP1G1LR","output_index":0,"sequence_number":87}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" area","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"IoFxhHT0S2D","output_index":0,"sequence_number":88}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" of","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"8ocFOGBmBxLAy","output_index":0,"sequence_number":89}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" expertise","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"MsxIJs","output_index":0,"sequence_number":90}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".\n\n","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"0hXVHSxmEzAfo","output_index":0,"sequence_number":91}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"2","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"kYR0FdWcxaVIyoT","output_index":0,"sequence_number":92}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"8AVkzTH5oQ2Ea3w","output_index":0,"sequence_number":93}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" **","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"uSEIHZyUCn6Ns","output_index":0,"sequence_number":94}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"Sur","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"P73cMx6kWmrpf","output_index":0,"sequence_number":95}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"prise","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"3x0V86slZfc","output_index":0,"sequence_number":96}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" Element","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"P54ucKKE","output_index":0,"sequence_number":97}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"**","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"Y4gTEKEAXxQd5Z","output_index":0,"sequence_number":98}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":":","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"mb4rbxmph7FBfFY","output_index":0,"sequence_number":99}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" The","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"WOQucBmTB3W1","output_index":0,"sequence_number":100}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" punch","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"dh6riwNrDQ","output_index":0,"sequence_number":101}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"line","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"dG8x2aWeLBvy","output_index":0,"sequence_number":102}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" delivers","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"AvywpI0","output_index":0,"sequence_number":103}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" an","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"x7bDi4kmePshO","output_index":0,"sequence_number":104}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" unexpected","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"aa13X","output_index":0,"sequence_number":105}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" twist","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"5vWJPzoyXJ","output_index":0,"sequence_number":106}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"I4SgVqsdgh4Iq9y","output_index":0,"sequence_number":107}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" You","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"QmG22ploL4PA","output_index":0,"sequence_number":108}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" expect","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"d7pmncL1I","output_index":0,"sequence_number":109}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"DE3zEEd48D60","output_index":0,"sequence_number":110}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" award","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"9emuHJ8kzC","output_index":0,"sequence_number":111}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" to","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"zLlgDWd6XZnBI","output_index":0,"sequence_number":112}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" be","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"IofL9iR1fZWH7","output_index":0,"sequence_number":113}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" for","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"uZbOQUgwCQNS","output_index":0,"sequence_number":114}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" some","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"VdOVg200trS","output_index":0,"sequence_number":115}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" human","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"ZR1jijs6RR","output_index":0,"sequence_number":116}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" trait","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"YFiuWDRVqT","output_index":0,"sequence_number":117}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"yfYVyWUTwDCOlng","output_index":0,"sequence_number":118}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" but","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"fezlQ9HKgG29","output_index":0,"sequence_number":119}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" it's","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"kOKjHhMKvxo","output_index":0,"sequence_number":120}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" actually","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"8OzqVUl","output_index":0,"sequence_number":121}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" a","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"7ElfyBZnK0yTdq","output_index":0,"sequence_number":122}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" humorous","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"3hWMHah","output_index":0,"sequence_number":123}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" observation","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"eJyp","output_index":0,"sequence_number":124}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" about","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"NzbrTnXscy","output_index":0,"sequence_number":125}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"vEh4ykDzVtjw","output_index":0,"sequence_number":126}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" scare","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"DxDYdByBKX","output_index":0,"sequence_number":127}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"crow","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"b6cTjeCsdgS9","output_index":0,"sequence_number":128}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"’s","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"fA0DCqJ1zIPX7z","output_index":0,"sequence_number":129}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" existence","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"g60ZOk","output_index":0,"sequence_number":130}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".\n\n","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"Cy7j62pp0KmeC","output_index":0,"sequence_number":131}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"3","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"j2isSvjsvXEfLT8","output_index":0,"sequence_number":132}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"hwl3YJGsYuliUZc","output_index":0,"sequence_number":133}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" **","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"OW7wjSZuS9PUF","output_index":0,"sequence_number":134}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"Abs","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"hGDaoSd3EyQi0","output_index":0,"sequence_number":135}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"urd","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"kzwdZb5gdRBUO","output_index":0,"sequence_number":136}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"ity","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"AGB4ZWKhdAmpl","output_index":0,"sequence_number":137}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"**","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"AQM9tjRdYuiDxU","output_index":0,"sequence_number":138}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":":","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"zkwYjpymmS54zLL","output_index":0,"sequence_number":139}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" The","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"2bpD1VPjVqT4","output_index":0,"sequence_number":140}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" idea","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"yJrTH0IE5EI","output_index":0,"sequence_number":141}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" of","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"2F9lKnywGkXeg","output_index":0,"sequence_number":142}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" a","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"DeHfaCfUZ3OFUD","output_index":0,"sequence_number":143}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" scare","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"XbHJOoxc2T","output_index":0,"sequence_number":144}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"crow","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"5KhIZhunW2MB","output_index":0,"sequence_number":145}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"CUjg4FXgNB6fW9T","output_index":0,"sequence_number":146}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" an","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"nppy6fsrODqdD","output_index":0,"sequence_number":147}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" in","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"9f3xNqHJ31DbK","output_index":0,"sequence_number":148}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"animate","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"x5WNWGnkw","output_index":0,"sequence_number":149}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" object","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"JMehZgCZL","output_index":0,"sequence_number":150}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"G4moFDLqPgXl2og","output_index":0,"sequence_number":151}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" receiving","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"usujJs","output_index":0,"sequence_number":152}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" an","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"7rqwpfzZZwmpe","output_index":0,"sequence_number":153}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" award","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"ld5vgi60uy","output_index":0,"sequence_number":154}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" adds","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"kErKYzpCcOX","output_index":0,"sequence_number":155}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" an","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"1f6bhXZSy1GeE","output_index":0,"sequence_number":156}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" element","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"33nyGp9n","output_index":0,"sequence_number":157}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" of","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"YIa5Wv8NUAeAT","output_index":0,"sequence_number":158}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" absurd","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"s1Dxhug3I","output_index":0,"sequence_number":159}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"ity","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"RybQeNxIszXqy","output_index":0,"sequence_number":160}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"SKxMJyTX66sfon9","output_index":0,"sequence_number":161}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" making","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"SAXT80cOM","output_index":0,"sequence_number":162}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" it","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"tzZHDUqVepH96","output_index":0,"sequence_number":163}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" more","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"8qRMxic0p2b","output_index":0,"sequence_number":164}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" amusing","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"Zb7GsyKt","output_index":0,"sequence_number":165}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":".\n\n","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"31laY4QlnMB6y","output_index":0,"sequence_number":166}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"Overall","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"95bVDR9T0","output_index":0,"sequence_number":167}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":",","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"OhUixHaPQ5ebUzy","output_index":0,"sequence_number":168}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" it's","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"bbYLkiw2T8E","output_index":0,"sequence_number":169}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"ostR0cxyGIJD","output_index":0,"sequence_number":170}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" clever","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"PpGqKElOs","output_index":0,"sequence_number":171}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" word","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"I0DETY9xxgm","output_index":0,"sequence_number":172}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"play","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"6zWRZleG0DvD","output_index":0,"sequence_number":173}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" combined","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"buIFOKO","output_index":0,"sequence_number":174}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" with","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"32zyLmemqJP","output_index":0,"sequence_number":175}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" an","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"Ua7JQewv7wBMa","output_index":0,"sequence_number":176}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" unexpected","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"sFOzn","output_index":0,"sequence_number":177}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" twist","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"2VbhR1bqcr","output_index":0,"sequence_number":178}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" that","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"F7jlTqm5mqb","output_index":0,"sequence_number":179}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" makes","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"Ywx6KbSzzU","output_index":0,"sequence_number":180}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"B4aGSKflNN22","output_index":0,"sequence_number":181}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" joke","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"hNMEMTZL5Ja","output_index":0,"sequence_number":182}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" effective","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"bsB12A","output_index":0,"sequence_number":183}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"!","item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"obfuscation":"pjObCPZ3LfG6WVF","output_index":0,"sequence_number":184}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","logprobs":[],"output_index":0,"sequence_number":185,"text":"The joke is funny because it uses a play on words, which is a common form of humor. \n\n1. **Double Meaning**: The phrase \"outstanding in his field\" can be interpreted literally, meaning the scarecrow is literally standing out in a field (as that's where scarecrows are found). However, it also has a figurative meaning: it suggests that someone is exceptionally skilled or accomplished in their area of expertise.\n\n2. **Surprise Element**: The punchline delivers an unexpected twist. You expect the award to be for some human trait, but it's actually a humorous observation about the scarecrow’s existence.\n\n3. **Absurdity**: The idea of a scarecrow, an inanimate object, receiving an award adds an element of absurdity, making it more amusing.\n\nOverall, it's the clever wordplay combined with an unexpected twist that makes the joke effective!"}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"The joke is funny because it uses a play on words, which is a common form of humor. \n\n1. **Double Meaning**: The phrase \"outstanding in his field\" can be interpreted literally, meaning the scarecrow is literally standing out in a field (as that's where scarecrows are found). However, it also has a figurative meaning: it suggests that someone is exceptionally skilled or accomplished in their area of expertise.\n\n2. **Surprise Element**: The punchline delivers an unexpected twist. You expect the award to be for some human trait, but it's actually a humorous observation about the scarecrow’s existence.\n\n3. **Absurdity**: The idea of a scarecrow, an inanimate object, receiving an award adds an element of absurdity, making it more amusing.\n\nOverall, it's the clever wordplay combined with an unexpected twist that makes the joke effective!"},"sequence_number":186}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The joke is funny because it uses a play on words, which is a common form of humor. \n\n1. **Double Meaning**: The phrase \"outstanding in his field\" can be interpreted literally, meaning the scarecrow is literally standing out in a field (as that's where scarecrows are found). However, it also has a figurative meaning: it suggests that someone is exceptionally skilled or accomplished in their area of expertise.\n\n2. **Surprise Element**: The punchline delivers an unexpected twist. You expect the award to be for some human trait, but it's actually a humorous observation about the scarecrow’s existence.\n\n3. **Absurdity**: The idea of a scarecrow, an inanimate object, receiving an award adds an element of absurdity, making it more amusing.\n\nOverall, it's the clever wordplay combined with an unexpected twist that makes the joke effective!"}],"role":"assistant"},"output_index":0,"sequence_number":187}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","response":{"id":"resp_0f9c4b2f224d858000695fa0649b8c8197b38914b15a7add0e","object":"response","created_at":1767874660,"status":"completed","background":false,"completed_at":1767874663,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[{"id":"msg_0f9c4b2f224d858000695fa064f1dc81979e4a37fab905af69","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"The joke is funny because it uses a play on words, which is a common form of humor. \n\n1. **Double Meaning**: The phrase \"outstanding in his field\" can be interpreted literally, meaning the scarecrow is literally standing out in a field (as that's where scarecrows are found). However, it also has a figurative meaning: it suggests that someone is exceptionally skilled or accomplished in their area of expertise.\n\n2. **Surprise Element**: The punchline delivers an unexpected twist. You expect the award to be for some human trait, but it's actually a humorous observation about the scarecrow’s existence.\n\n3. **Absurdity**: The idea of a scarecrow, an inanimate object, receiving an award adds an element of absurdity, making it more amusing.\n\nOverall, it's the clever wordplay combined with an unexpected twist that makes the joke effective!"}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":"resp_0f9c4b2f224d858000695fa062bf048197a680f357bbb09000","prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":43,"input_tokens_details":{"cached_tokens":0},"output_tokens":182,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":225},"user":null,"metadata":{}},"sequence_number":188}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "tell me a joke",
|
||||
"model": "gpt-4o-mini",
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_0f9c4b2f224d858000695fa062bf048197a680f357bbb09000","object":"response","created_at":1767874658,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_0f9c4b2f224d858000695fa062bf048197a680f357bbb09000","object":"response","created_at":1767874658,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":0,"sequence_number":2}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""},"sequence_number":3}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"Why","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"N16SG5UiLncOU","output_index":0,"sequence_number":4}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" did","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"OpojJ3pv0h55","output_index":0,"sequence_number":5}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" the","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"11RCrnBxLo5x","output_index":0,"sequence_number":6}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" scare","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"QZrRBlk6BV","output_index":0,"sequence_number":7}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"crow","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"gp7F8IVupiHG","output_index":0,"sequence_number":8}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" win","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"uKq4X8mT1jl9","output_index":0,"sequence_number":9}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" an","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"2Ox5JzaAsJHuT","output_index":0,"sequence_number":10}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" award","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"ZOQbZabNAQ","output_index":0,"sequence_number":11}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"?\n\n","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"N2dSd0FHBxooR","output_index":0,"sequence_number":12}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"Because","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"LZ1O4laHt","output_index":0,"sequence_number":13}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" he","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"dqcS6ePaMvxMD","output_index":0,"sequence_number":14}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" was","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"nR6CtC7MUsWW","output_index":0,"sequence_number":15}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" outstanding","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"dNVG","output_index":0,"sequence_number":16}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" in","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"P7w4jjOcdVOla","output_index":0,"sequence_number":17}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" his","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"u9dg4RLIld4e","output_index":0,"sequence_number":18}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":" field","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"qefuqzOCOy","output_index":0,"sequence_number":19}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","content_index":0,"delta":"!","item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"obfuscation":"DT9j4dSh0xyJdxU","output_index":0,"sequence_number":20}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","logprobs":[],"output_index":0,"sequence_number":21,"text":"Why did the scarecrow win an award?\n\nBecause he was outstanding in his field!"}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"Why did the scarecrow win an award?\n\nBecause he was outstanding in his field!"},"sequence_number":22}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"Why did the scarecrow win an award?\n\nBecause he was outstanding in his field!"}],"role":"assistant"},"output_index":0,"sequence_number":23}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","response":{"id":"resp_0f9c4b2f224d858000695fa062bf048197a680f357bbb09000","object":"response","created_at":1767874658,"status":"completed","background":false,"completed_at":1767874660,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[{"id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"Why did the scarecrow win an award?\n\nBecause he was outstanding in his field!"}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":11,"input_tokens_details":{"cached_tokens":0},"output_tokens":18,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":29},"user":null,"metadata":{}},"sequence_number":24}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
-- request --
|
||||
{
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Is 3 + 5 a prime number? Also add 10 + 20. Use the add function for both."
|
||||
}
|
||||
],
|
||||
"model": "gpt-4.1",
|
||||
"stream": true,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "add",
|
||||
"description": "Add two numbers together.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_parallel_streaming_001","object":"response","created_at":1767875312,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_parallel_streaming_001","object":"response","created_at":1767875312,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"rs_parallel_streaming_reasoning_001","type":"reasoning","status":"in_progress","summary":[]},"output_index":0,"sequence_number":2}
|
||||
|
||||
event: response.reasoning_summary_part.added
|
||||
data: {"type":"response.reasoning_summary_part.added","item_id":"rs_parallel_streaming_reasoning_001","output_index":0,"part":{"type":"summary_text","text":""},"summary_index":0,"sequence_number":3}
|
||||
|
||||
event: response.reasoning_summary_text.delta
|
||||
data: {"type":"response.reasoning_summary_text.delta","item_id":"rs_parallel_streaming_reasoning_001","output_index":0,"summary_index":0,"delta":"The user wants two additions: 3+5 and 10+20. I'll call add for both.","sequence_number":4}
|
||||
|
||||
event: response.reasoning_summary_text.done
|
||||
data: {"type":"response.reasoning_summary_text.done","item_id":"rs_parallel_streaming_reasoning_001","output_index":0,"summary_index":0,"text":"The user wants two additions: 3+5 and 10+20. I'll call add for both.","sequence_number":5}
|
||||
|
||||
event: response.reasoning_summary_part.done
|
||||
data: {"type":"response.reasoning_summary_part.done","item_id":"rs_parallel_streaming_reasoning_001","output_index":0,"part":{"type":"summary_text","text":"The user wants two additions: 3+5 and 10+20. I'll call add for both."},"summary_index":0,"sequence_number":6}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"rs_parallel_streaming_reasoning_001","type":"reasoning","status":"completed","summary":[{"type":"summary_text","text":"The user wants two additions: 3+5 and 10+20. I'll call add for both."}]},"output_index":0,"sequence_number":7}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"fc_parallel_streaming_first_001","type":"function_call","status":"in_progress","arguments":"","call_id":"call_ParallelStreamFirst001","name":"add"},"output_index":1,"sequence_number":8}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"{\"a\":3,\"b\":5}","item_id":"fc_parallel_streaming_first_001","output_index":1,"sequence_number":9}
|
||||
|
||||
event: response.function_call_arguments.done
|
||||
data: {"type":"response.function_call_arguments.done","arguments":"{\"a\":3,\"b\":5}","item_id":"fc_parallel_streaming_first_001","output_index":1,"sequence_number":10}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"fc_parallel_streaming_first_001","type":"function_call","status":"completed","arguments":"{\"a\":3,\"b\":5}","call_id":"call_ParallelStreamFirst001","name":"add"},"output_index":1,"sequence_number":11}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"fc_parallel_streaming_second_001","type":"function_call","status":"in_progress","arguments":"","call_id":"call_ParallelStreamSecond01","name":"add"},"output_index":2,"sequence_number":12}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"{\"a\":10,\"b\":20}","item_id":"fc_parallel_streaming_second_001","output_index":2,"sequence_number":13}
|
||||
|
||||
event: response.function_call_arguments.done
|
||||
data: {"type":"response.function_call_arguments.done","arguments":"{\"a\":10,\"b\":20}","item_id":"fc_parallel_streaming_second_001","output_index":2,"sequence_number":14}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"fc_parallel_streaming_second_001","type":"function_call","status":"completed","arguments":"{\"a\":10,\"b\":20}","call_id":"call_ParallelStreamSecond01","name":"add"},"output_index":2,"sequence_number":15}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","response":{"id":"resp_parallel_streaming_001","object":"response","created_at":1767875312,"status":"completed","background":false,"completed_at":1767875312,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4.1-2025-04-14","output":[{"id":"rs_parallel_streaming_reasoning_001","type":"reasoning","status":"completed","summary":[{"type":"summary_text","text":"The user wants two additions: 3+5 and 10+20. I'll call add for both."}]},{"id":"fc_parallel_streaming_first_001","type":"function_call","status":"completed","arguments":"{\"a\":3,\"b\":5}","call_id":"call_ParallelStreamFirst001","name":"add"},{"id":"fc_parallel_streaming_second_001","type":"function_call","status":"completed","arguments":"{\"a\":10,\"b\":20}","call_id":"call_ParallelStreamSecond01","name":"add"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":65,"input_tokens_details":{"cached_tokens":0},"output_tokens":30,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":95},"user":null,"metadata":{}},"sequence_number":16}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,20 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "hello_stream_error",
|
||||
"model": "gpt-6.7",
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_123","object":"response","status":"in_progress","error":null,"output":[]},"sequence_number":1}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_123","object":"response","status":"in_progress","error":null,"output":[]},"sequence_number":2}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","item_id":"msg_123","output_index":0,"content_index":0,"delta":"Hello","sequence_number":3}
|
||||
|
||||
event: error
|
||||
data: {"type":"error","code":"ERR_SOMETHING","message":"Something went wrong","param":null,"sequence_number":4}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "hello_stream_failure",
|
||||
"model": "gpt-6.7",
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_123","object":"response","status":"in_progress","error":null,"output":[]},"sequence_number":1}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_123","object":"response","status":"in_progress","error":null,"output":[]},"sequence_number":2}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","item_id":"msg_123","output_index":0,"content_index":0,"delta":"Hello","sequence_number":3}
|
||||
|
||||
event: response.failed
|
||||
data: {"type":"response.failed","response":{"id":"resp_123","object":"response","status":"failed","error":{"code":"server_error","message":"The model failed to generate a response."},"output":[]},"sequence_number":4}
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
Both a reasoning summary and a commentary message before a function_call.
|
||||
|
||||
-- request --
|
||||
{
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Is 3 + 5 a prime number? Use the add function to calculate the sum."
|
||||
}
|
||||
],
|
||||
"model": "gpt-5.4",
|
||||
"stream": true,
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "add",
|
||||
"description": "Add two numbers together.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "number"
|
||||
},
|
||||
"b": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_1bba3bc54ed351c41270c26831354d920fcc75088476e53de6","object":"response","created_at":1773229900,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.4-2026-03-05","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"xhigh","summary":null},"safety_identifier":null,"service_tier":"default","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"low"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":0.98,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_1bba3bc54ed351c41270c26831354d920fcc75088476e53de6","object":"response","created_at":1773229900,"status":"in_progress","background":false,"completed_at":null,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.4-2026-03-05","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"xhigh","summary":null},"safety_identifier":null,"service_tier":"default","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"low"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":0.98,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"rs_1bba3bc54ed351c41270c26831908d920fcc75088476e53de6","type":"reasoning","status":"in_progress","summary":[]},"output_index":0,"sequence_number":2}
|
||||
|
||||
event: response.reasoning_summary_part.added
|
||||
data: {"type":"response.reasoning_summary_part.added","item_id":"rs_1bba3bc54ed351c41270c26831908d920fcc75088476e53de6","output_index":0,"part":{"type":"summary_text","text":""},"summary_index":0,"sequence_number":3}
|
||||
|
||||
event: response.reasoning_summary_text.delta
|
||||
data: {"type":"response.reasoning_summary_text.delta","item_id":"rs_1bba3bc54ed351c41270c26831908d920fcc75088476e53de6","output_index":0,"summary_index":0,"delta":"I need to add 3 and 5 to check primality.","sequence_number":4}
|
||||
|
||||
event: response.reasoning_summary_text.done
|
||||
data: {"type":"response.reasoning_summary_text.done","item_id":"rs_1bba3bc54ed351c41270c26831908d920fcc75088476e53de6","output_index":0,"summary_index":0,"text":"I need to add 3 and 5 to check primality.","sequence_number":5}
|
||||
|
||||
event: response.reasoning_summary_part.done
|
||||
data: {"type":"response.reasoning_summary_part.done","item_id":"rs_1bba3bc54ed351c41270c26831908d920fcc75088476e53de6","output_index":0,"part":{"type":"summary_text","text":"I need to add 3 and 5 to check primality."},"summary_index":0,"sequence_number":6}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"rs_1bba3bc54ed351c41270c26831908d920fcc75088476e53de6","type":"reasoning","status":"completed","encrypted_content":"gAAAAA==","summary":[{"type":"summary_text","text":"I need to add 3 and 5 to check primality."}]},"output_index":0,"sequence_number":7}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"msg_1bba3bc54ed351c41270c26831a09d920fdd86199587f64ef7","type":"message","status":"in_progress","content":[],"phase":"commentary","role":"assistant"},"output_index":1,"sequence_number":8}
|
||||
|
||||
event: response.content_part.added
|
||||
data: {"type":"response.content_part.added","item_id":"msg_1bba3bc54ed351c41270c26831a09d920fdd86199587f64ef7","output_index":1,"content_index":0,"part":{"type":"output_text","text":"","annotations":[]},"sequence_number":9}
|
||||
|
||||
event: response.output_text.delta
|
||||
data: {"type":"response.output_text.delta","item_id":"msg_1bba3bc54ed351c41270c26831a09d920fdd86199587f64ef7","output_index":1,"content_index":0,"delta":"Let me calculate the sum first using the add function.","sequence_number":10}
|
||||
|
||||
event: response.output_text.done
|
||||
data: {"type":"response.output_text.done","item_id":"msg_1bba3bc54ed351c41270c26831a09d920fdd86199587f64ef7","output_index":1,"content_index":0,"text":"Let me calculate the sum first using the add function.","sequence_number":11}
|
||||
|
||||
event: response.content_part.done
|
||||
data: {"type":"response.content_part.done","item_id":"msg_1bba3bc54ed351c41270c26831a09d920fdd86199587f64ef7","output_index":1,"content_index":0,"part":{"type":"output_text","text":"Let me calculate the sum first using the add function.","annotations":[]},"sequence_number":12}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"msg_1bba3bc54ed351c41270c26831a09d920fdd86199587f64ef7","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"text":"Let me calculate the sum first using the add function."}],"phase":"commentary","role":"assistant"},"output_index":1,"sequence_number":13}
|
||||
|
||||
event: response.output_item.added
|
||||
data: {"type":"response.output_item.added","item":{"id":"fc_1bba3bc54ed351c41270c26831b0ad920fee97200698074f08","type":"function_call","status":"in_progress","arguments":"","call_id":"call_B9UjYX01Lvvv1XwjDsdmRW3f","name":"add"},"output_index":2,"sequence_number":14}
|
||||
|
||||
event: response.function_call_arguments.delta
|
||||
data: {"type":"response.function_call_arguments.delta","delta":"{\"a\":3,\"b\":5}","item_id":"fc_1bba3bc54ed351c41270c26831b0ad920fee97200698074f08","output_index":2,"sequence_number":15}
|
||||
|
||||
event: response.function_call_arguments.done
|
||||
data: {"type":"response.function_call_arguments.done","arguments":"{\"a\":3,\"b\":5}","item_id":"fc_1bba3bc54ed351c41270c26831b0ad920fee97200698074f08","output_index":2,"sequence_number":16}
|
||||
|
||||
event: response.output_item.done
|
||||
data: {"type":"response.output_item.done","item":{"id":"fc_1bba3bc54ed351c41270c26831b0ad920fee97200698074f08","type":"function_call","status":"completed","arguments":"{\"a\":3,\"b\":5}","call_id":"call_B9UjYX01Lvvv1XwjDsdmRW3f","name":"add"},"output_index":2,"sequence_number":17}
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","response":{"id":"resp_1bba3bc54ed351c41270c26831354d920fcc75088476e53de6","object":"response","created_at":1773229900,"status":"completed","background":false,"completed_at":1773229905,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-5.4-2026-03-05","output":[{"id":"rs_1bba3bc54ed351c41270c26831908d920fcc75088476e53de6","type":"reasoning","status":"completed","encrypted_content":"gAAAAA==","summary":[{"type":"summary_text","text":"I need to add 3 and 5 to check primality."}]},{"id":"msg_1bba3bc54ed351c41270c26831a09d920fdd86199587f64ef7","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"text":"Let me calculate the sum first using the add function."}],"phase":"commentary","role":"assistant"},{"id":"fc_1bba3bc54ed351c41270c26831b0ad920fee97200698074f08","type":"function_call","status":"completed","arguments":"{\"a\":3,\"b\":5}","call_id":"call_B9UjYX01Lvvv1XwjDsdmRW3f","name":"add"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"xhigh","summary":null},"safety_identifier":null,"service_tier":"default","store":false,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"low"},"tool_choice":"auto","tools":[{"type":"function","description":"Add two numbers together.","name":"add","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":0.98,"truncation":"disabled","usage":{"input_tokens":58,"input_tokens_details":{"cached_tokens":0},"output_tokens":35,"output_tokens_details":{"reasoning_tokens":10},"total_tokens":93},"user":null,"metadata":{}},"sequence_number":18}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
-- request --
|
||||
{
|
||||
"input": "hello_wrong_format",
|
||||
"model": "gpt-6.7",
|
||||
"stream": true
|
||||
}
|
||||
|
||||
-- streaming --
|
||||
event: response.created
|
||||
data: {"type":"response.created","response":{"id":"resp_123","object":"response","status":"in_progress","error":null,"output":[]},"sequence_number":1}
|
||||
|
||||
event: response.in_progress
|
||||
data: {"type":"response.in_progress","response":{"id":"resp_123","object":"response","status":"in_progress","error":null,"output":[]},"sequence_number":2}
|
||||
|
||||
event: response.output_text.delta
|
||||
da
|
||||
ta: { "wrong format": should be forwarded as received
|
||||
|
||||
event: response.completed
|
||||
data: {"type":"response.completed","response":{"id":"resp_123","object":"response","created_at":1767874658,"status":"completed","background":false,"completed_at":1767874660,"error":null,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-mini-2024-07-18","output":[{"id":"msg_0f9c4b2f224d858000695fa063d4708197af73c2f37cb0b9d3","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"Why did the scarecrow win an award?\n\nBecause he was outstanding in his field!"}],"role":"assistant"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":11,"input_tokens_details":{"cached_tokens":0},"output_tokens":18,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":29},"user":null,"metadata":{}},"sequence_number":24}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package intercept
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
ant_option "github.com/anthropics/anthropic-sdk-go/option"
|
||||
oai_option "github.com/openai/openai-go/v3/option"
|
||||
|
||||
"github.com/coder/coder/v2/aibridge/context"
|
||||
)
|
||||
|
||||
const (
|
||||
prefix = "X-AI-Bridge-Actor"
|
||||
)
|
||||
|
||||
func ActorIDHeader() string {
|
||||
return fmt.Sprintf("%s-ID", prefix)
|
||||
}
|
||||
|
||||
func ActorMetadataHeader(name string) string {
|
||||
return fmt.Sprintf("%s-Metadata-%s", prefix, name)
|
||||
}
|
||||
|
||||
func IsActorHeader(name string) bool {
|
||||
return strings.HasPrefix(strings.ToLower(name), strings.ToLower(prefix))
|
||||
}
|
||||
|
||||
// ActorHeadersAsOpenAIOpts produces a slice of headers using OpenAI's RequestOption type.
|
||||
func ActorHeadersAsOpenAIOpts(actor *context.Actor) []oai_option.RequestOption {
|
||||
var opts []oai_option.RequestOption
|
||||
|
||||
headers := headersFromActor(actor)
|
||||
if len(headers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for k, v := range headers {
|
||||
// [k] will be canonicalized, see [http.Header]'s [Add] method.
|
||||
opts = append(opts, oai_option.WithHeaderAdd(k, v))
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
// ActorHeadersAsAnthropicOpts produces a slice of headers using Anthropic's RequestOption type.
|
||||
func ActorHeadersAsAnthropicOpts(actor *context.Actor) []ant_option.RequestOption {
|
||||
var opts []ant_option.RequestOption
|
||||
|
||||
headers := headersFromActor(actor)
|
||||
if len(headers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for k, v := range headers {
|
||||
// [k] will be canonicalized, see [http.Header]'s [Add] method.
|
||||
opts = append(opts, ant_option.WithHeaderAdd(k, v))
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
// headersFromActor produces a map of headers from a given [context.Actor].
|
||||
func headersFromActor(actor *context.Actor) map[string]string {
|
||||
if actor == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
headers := make(map[string]string, len(actor.Metadata)+1)
|
||||
|
||||
// Add actor ID.
|
||||
headers[ActorIDHeader()] = actor.ID
|
||||
|
||||
// Add headers for provided metadata.
|
||||
for k, v := range actor.Metadata {
|
||||
headers[ActorMetadataHeader(k)] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package intercept_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/aibridge/context"
|
||||
"github.com/coder/coder/v2/aibridge/intercept"
|
||||
"github.com/coder/coder/v2/aibridge/recorder"
|
||||
)
|
||||
|
||||
func TestNilActor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Nil(t, intercept.ActorHeadersAsOpenAIOpts(nil))
|
||||
require.Nil(t, intercept.ActorHeadersAsAnthropicOpts(nil))
|
||||
}
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actorID := uuid.NewString()
|
||||
actor := &context.Actor{
|
||||
ID: actorID,
|
||||
}
|
||||
|
||||
// We can't peek inside since these opts require an internal type to apply onto.
|
||||
// All we can do is check the length.
|
||||
// See TestActorHeaders for an integration test.
|
||||
oaiOpts := intercept.ActorHeadersAsOpenAIOpts(actor)
|
||||
require.Len(t, oaiOpts, 1)
|
||||
antOpts := intercept.ActorHeadersAsAnthropicOpts(actor)
|
||||
require.Len(t, antOpts, 1)
|
||||
}
|
||||
|
||||
func TestBasicAndMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actorID := uuid.NewString()
|
||||
actor := &context.Actor{
|
||||
ID: actorID,
|
||||
Metadata: recorder.Metadata{
|
||||
"This": "That",
|
||||
"And": "The other",
|
||||
},
|
||||
}
|
||||
|
||||
// We can't peek inside since these opts require an internal type to apply onto.
|
||||
// All we can do is check the length.
|
||||
// See TestActorHeaders for an integration test.
|
||||
oaiOpts := intercept.ActorHeadersAsOpenAIOpts(actor)
|
||||
require.Len(t, oaiOpts, 1+len(actor.Metadata))
|
||||
antOpts := intercept.ActorHeadersAsAnthropicOpts(actor)
|
||||
require.Len(t, antOpts, 1+len(actor.Metadata))
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
package apidump
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/tidwall/pretty"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"cdr.dev/slog/v3"
|
||||
"github.com/coder/quartz"
|
||||
)
|
||||
|
||||
const (
|
||||
// SuffixRequest is the file suffix for request dump files.
|
||||
SuffixRequest = ".req.txt"
|
||||
// SuffixResponse is the file suffix for response dump files.
|
||||
SuffixResponse = ".resp.txt"
|
||||
)
|
||||
|
||||
// MiddlewareNext is the function to call the next middleware or the actual request.
|
||||
type MiddlewareNext = func(*http.Request) (*http.Response, error)
|
||||
|
||||
// Middleware is an HTTP middleware function compatible with SDK WithMiddleware options.
|
||||
type Middleware = func(*http.Request, MiddlewareNext) (*http.Response, error)
|
||||
|
||||
// NewBridgeMiddleware returns a middleware function that dumps requests and responses to files.
|
||||
// If baseDir is empty, returns nil (no middleware).
|
||||
func NewBridgeMiddleware(baseDir string, provider string, model string, interceptionID uuid.UUID, logger slog.Logger, clk quartz.Clock) Middleware {
|
||||
if baseDir == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
d := &dumper{
|
||||
dumpPath: interceptDumpPath(baseDir, provider, model, interceptionID, clk),
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
return func(req *http.Request, next MiddlewareNext) (*http.Response, error) {
|
||||
if err := d.dumpRequest(req); err != nil {
|
||||
logger.Named("apidump").Warn(req.Context(), "failed to dump request", slog.Error(err))
|
||||
}
|
||||
|
||||
// TODO: https://github.com/coder/aibridge/issues/129
|
||||
resp, err := next(req)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if err := d.dumpResponse(resp); err != nil {
|
||||
logger.Named("apidump").Warn(req.Context(), "failed to dump response", slog.Error(err))
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
type dumper struct {
|
||||
dumpPath string
|
||||
logger slog.Logger
|
||||
}
|
||||
|
||||
func (d *dumper) dumpRequest(req *http.Request) error {
|
||||
dumpPath := d.dumpPath + SuffixRequest
|
||||
if err := os.MkdirAll(filepath.Dir(dumpPath), 0o755); err != nil {
|
||||
return xerrors.Errorf("create dump dir: %w", err)
|
||||
}
|
||||
|
||||
// Read and restore body
|
||||
var bodyBytes []byte
|
||||
if req.Body != nil {
|
||||
var err error
|
||||
bodyBytes, err = io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("read request body: %w", err)
|
||||
}
|
||||
req.Body = io.NopCloser(bytes.NewReader(bodyBytes))
|
||||
}
|
||||
|
||||
prettyBody := prettyPrintJSON(bodyBytes)
|
||||
|
||||
// Build raw HTTP request format
|
||||
var buf bytes.Buffer
|
||||
_, err := fmt.Fprintf(&buf, "%s %s %s\r\n", req.Method, req.URL.RequestURI(), req.Proto)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("write request uri: %w", err)
|
||||
}
|
||||
err = d.writeRedactedHeaders(&buf, req.Header, sensitiveRequestHeaders, map[string]string{
|
||||
"Content-Length": fmt.Sprintf("%d", len(prettyBody)),
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("write request headers: %w", err)
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintf(&buf, "\r\n")
|
||||
if err != nil {
|
||||
return xerrors.Errorf("write request header terminator: %w", err)
|
||||
}
|
||||
// bytes.Buffer writes to in-memory storage and never return errors.
|
||||
_, _ = buf.Write(prettyBody)
|
||||
_ = buf.WriteByte('\n')
|
||||
|
||||
return os.WriteFile(dumpPath, buf.Bytes(), 0o600)
|
||||
}
|
||||
|
||||
func (d *dumper) dumpResponse(resp *http.Response) error {
|
||||
dumpPath := d.dumpPath + SuffixResponse
|
||||
|
||||
// Build raw HTTP response headers
|
||||
var headerBuf bytes.Buffer
|
||||
_, err := fmt.Fprintf(&headerBuf, "%s %s\r\n", resp.Proto, resp.Status)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("write response status: %w", err)
|
||||
}
|
||||
err = d.writeRedactedHeaders(&headerBuf, resp.Header, sensitiveResponseHeaders, nil)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("write response headers: %w", err)
|
||||
}
|
||||
_, err = fmt.Fprintf(&headerBuf, "\r\n")
|
||||
if err != nil {
|
||||
return xerrors.Errorf("write response header terminator: %w", err)
|
||||
}
|
||||
|
||||
if resp.Body == nil {
|
||||
// No body, just write headers
|
||||
return os.WriteFile(dumpPath, headerBuf.Bytes(), 0o600)
|
||||
}
|
||||
|
||||
// Wrap the response body to capture it as it streams
|
||||
resp.Body = &streamingBodyDumper{
|
||||
body: resp.Body,
|
||||
dumpPath: dumpPath,
|
||||
headerData: headerBuf.Bytes(),
|
||||
logger: func(err error) {
|
||||
d.logger.Named("apidump").Warn(context.Background(), "failed to initialize response dump", slog.Error(err))
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeRedactedHeaders writes HTTP headers in wire format (Key: Value\r\n) to w,
|
||||
// redacting sensitive values and applying any overrides. Headers are sorted by key
|
||||
// for deterministic output.
|
||||
// `sensitive` and `overrides` must both supply keys in canoncialized form.
|
||||
// See [textproto.MIMEHeader].
|
||||
func (*dumper) writeRedactedHeaders(w io.Writer, headers http.Header, sensitive map[string]struct{}, overrides map[string]string) error {
|
||||
// Collect all header keys including overrides.
|
||||
headerKeys := make([]string, 0, len(headers)+len(overrides))
|
||||
seen := make(map[string]struct{}, len(headers)+len(overrides))
|
||||
for key := range headers {
|
||||
headerKeys = append(headerKeys, key)
|
||||
seen[key] = struct{}{}
|
||||
}
|
||||
// Add override keys that don't exist in headers.
|
||||
for key := range overrides {
|
||||
if _, ok := seen[key]; !ok {
|
||||
headerKeys = append(headerKeys, key)
|
||||
}
|
||||
}
|
||||
slices.Sort(headerKeys)
|
||||
|
||||
for _, key := range headerKeys {
|
||||
_, isSensitive := sensitive[key]
|
||||
values := headers[key]
|
||||
// If no values exist but we have an override, use that.
|
||||
if len(values) == 0 {
|
||||
if override, ok := overrides[key]; ok {
|
||||
_, err := fmt.Fprintf(w, "%s: %s\r\n", key, override)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("write response header override: %w", err)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
for _, value := range values {
|
||||
if override, ok := overrides[key]; ok {
|
||||
value = override
|
||||
}
|
||||
|
||||
if isSensitive {
|
||||
value = redactHeaderValue(value)
|
||||
}
|
||||
_, err := fmt.Fprintf(w, "%s: %s\r\n", key, value)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("write response headers: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// interceptDumpPath returns the base file path (without req/resp suffix) for an interception dump.
|
||||
func interceptDumpPath(baseDir string, provider string, model string, interceptionID uuid.UUID, clk quartz.Clock) string {
|
||||
safeModel := strings.ReplaceAll(model, "/", "-")
|
||||
return filepath.Join(baseDir, provider, safeModel, fmt.Sprintf("%d-%s", clk.Now().UTC().UnixMilli(), interceptionID))
|
||||
}
|
||||
|
||||
// passthroughDumpPath returns the base file path (without req/resp suffix) for a passthrough dump.
|
||||
func passthroughDumpPath(baseDir string, provider string, urlPath string, clk quartz.Clock) string {
|
||||
safeURLPath := strings.ReplaceAll(strings.TrimPrefix(urlPath, "/"), "/", "-")
|
||||
return filepath.Join(baseDir, provider, "passthrough", fmt.Sprintf("%d-%s-%s", clk.Now().UTC().UnixMilli(), safeURLPath, uuid.NewString()[:4]))
|
||||
}
|
||||
|
||||
// NewPassthroughMiddleware returns http.RoundTripper that dumps requests and responses to files.
|
||||
// If baseDir is empty, returns the original transport unchanged.
|
||||
// Used for logging in pass through routes.
|
||||
func NewPassthroughMiddleware(transport http.RoundTripper, baseDir string, provider string, logger slog.Logger, clk quartz.Clock) http.RoundTripper {
|
||||
if baseDir == "" {
|
||||
return transport
|
||||
}
|
||||
return &dumpRoundTripper{
|
||||
inner: transport,
|
||||
baseDir: baseDir,
|
||||
provider: provider,
|
||||
clk: clk,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
type dumpRoundTripper struct {
|
||||
inner http.RoundTripper
|
||||
baseDir string
|
||||
provider string
|
||||
clk quartz.Clock
|
||||
logger slog.Logger
|
||||
}
|
||||
|
||||
func (rt *dumpRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
dumper := dumper{
|
||||
dumpPath: passthroughDumpPath(rt.baseDir, rt.provider, req.URL.Path, rt.clk),
|
||||
logger: rt.logger,
|
||||
}
|
||||
|
||||
if err := dumper.dumpRequest(req); err != nil {
|
||||
dumper.logger.Named("apidump").Warn(req.Context(), "failed to dump passthrough request", slog.Error(err))
|
||||
}
|
||||
|
||||
resp, err := rt.inner.RoundTrip(req)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if err := dumper.dumpResponse(resp); err != nil {
|
||||
dumper.logger.Named("apidump").Warn(req.Context(), "failed to dump passthrough response", slog.Error(err))
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// prettyPrintJSON returns indented JSON if body is valid JSON, otherwise returns body as-is.
|
||||
// Unlike json.MarshalIndent, this preserves the original key order from the input,
|
||||
// which makes the dumps easier to read and compare with the original requests.
|
||||
func prettyPrintJSON(body []byte) []byte {
|
||||
if len(body) == 0 {
|
||||
return body
|
||||
}
|
||||
|
||||
result := body
|
||||
if json.Valid(body) {
|
||||
result = pretty.Pretty(body)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,466 @@
|
||||
package apidump //nolint:testpackage // tests unexported internals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cdr.dev/slog/v3"
|
||||
"cdr.dev/slog/v3/sloggers/slogtest"
|
||||
"github.com/coder/quartz"
|
||||
)
|
||||
|
||||
// findDumpFile finds a dump file matching the pattern in the given directory.
|
||||
func findDumpFile(t *testing.T, dir, suffix string) string {
|
||||
t.Helper()
|
||||
pattern := filepath.Join(dir, "*"+suffix)
|
||||
matches, err := filepath.Glob(pattern)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, matches, 1, "expected exactly one %s file in %s", suffix, dir)
|
||||
return matches[0]
|
||||
}
|
||||
|
||||
func TestBridgedMiddleware_RedactsSensitiveRequestHeaders(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
|
||||
clk := quartz.NewMock(t)
|
||||
interceptionID := uuid.New()
|
||||
|
||||
middleware := NewBridgeMiddleware(tmpDir, "openai", "gpt-4", interceptionID, logger, clk)
|
||||
require.NotNil(t, middleware)
|
||||
|
||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, "https://api.openai.com/v1/chat/completions", bytes.NewReader([]byte(`{"test": true}`)))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add sensitive headers that should be redacted
|
||||
req.Header.Set("Authorization", "Bearer sk-secret-key-12345")
|
||||
req.Header.Set("X-Api-Key", "secret-api-key-value")
|
||||
req.Header.Set("Cookie", "session=abc123")
|
||||
|
||||
// Add non-sensitive headers that should be kept as-is
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", "test-client")
|
||||
|
||||
// Call middleware with a mock next function
|
||||
resp, err := middleware(req, func(r *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Status: "200 OK",
|
||||
Proto: "HTTP/1.1",
|
||||
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(`{"ok": true}`))),
|
||||
}, nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Read the request dump file
|
||||
modelDir := filepath.Join(tmpDir, "openai", "gpt-4")
|
||||
reqDumpPath := findDumpFile(t, modelDir, SuffixRequest)
|
||||
reqContent, err := os.ReadFile(reqDumpPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
content := string(reqContent)
|
||||
|
||||
// Verify sensitive headers ARE present but redacted
|
||||
require.Contains(t, content, "Authorization: Bear...2345")
|
||||
require.Contains(t, content, "X-Api-Key: secr...alue")
|
||||
require.Contains(t, content, "Cookie: sess...c123") // "session=abc123" is 14 chars, so first 4 + last 4
|
||||
|
||||
// Verify the full secret values are NOT present
|
||||
require.NotContains(t, content, "sk-secret-key-12345")
|
||||
require.NotContains(t, content, "secret-api-key-value")
|
||||
|
||||
// Verify non-sensitive headers ARE present in full
|
||||
require.Contains(t, content, "Content-Type: application/json")
|
||||
require.Contains(t, content, "User-Agent: test-client")
|
||||
}
|
||||
|
||||
func TestBridgedMiddleware_RedactsSensitiveResponseHeaders(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
|
||||
clk := quartz.NewMock(t)
|
||||
interceptionID := uuid.New()
|
||||
|
||||
middleware := NewBridgeMiddleware(tmpDir, "openai", "gpt-4", interceptionID, logger, clk)
|
||||
require.NotNil(t, middleware)
|
||||
|
||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, "https://api.openai.com/v1/chat/completions", bytes.NewReader([]byte(`{}`)))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Call middleware with a response containing sensitive headers
|
||||
resp, err := middleware(req, func(r *http.Request) (*http.Response, error) {
|
||||
resp := &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Status: "200 OK",
|
||||
Proto: "HTTP/1.1",
|
||||
Header: make(http.Header),
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(`{"ok": true}`))),
|
||||
}
|
||||
// Add sensitive response headers
|
||||
resp.Header.Set("Set-Cookie", "session=secret123; HttpOnly; Secure")
|
||||
resp.Header.Set("WWW-Authenticate", "Bearer realm=\"api\"")
|
||||
// Add non-sensitive headers
|
||||
resp.Header.Set("Content-Type", "application/json")
|
||||
resp.Header.Set("X-Request-Id", "req-123")
|
||||
return resp, nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Must read and close response body to trigger the streaming dump
|
||||
_, err = io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
|
||||
// Read the response dump file
|
||||
modelDir := filepath.Join(tmpDir, "openai", "gpt-4")
|
||||
respDumpPath := findDumpFile(t, modelDir, SuffixResponse)
|
||||
respContent, err := os.ReadFile(respDumpPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
content := string(respContent)
|
||||
|
||||
// Verify sensitive headers are present but redacted
|
||||
require.Contains(t, content, "Set-Cookie: sess...cure")
|
||||
// Note: Go canonicalizes WWW-Authenticate to Www-Authenticate
|
||||
// "Bearer realm=\"api\"" = 18 chars, first 4 = "Bear", last 4 = "api\""
|
||||
require.Contains(t, content, "Www-Authenticate: Bear...api\"")
|
||||
|
||||
// Verify full secret values are NOT present
|
||||
require.NotContains(t, content, "secret123")
|
||||
require.NotContains(t, content, "realm=\"api\"")
|
||||
|
||||
// Verify non-sensitive headers ARE present in full
|
||||
require.Contains(t, content, "Content-Type: application/json")
|
||||
require.Contains(t, content, "X-Request-Id: req-123")
|
||||
}
|
||||
|
||||
func TestBridgedMiddleware_EmptyBaseDir_ReturnsNil(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
|
||||
middleware := NewBridgeMiddleware("", "openai", "gpt-4", uuid.New(), logger, quartz.NewMock(t))
|
||||
require.Nil(t, middleware)
|
||||
}
|
||||
|
||||
func TestBridgedMiddleware_PreservesRequestBody(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
|
||||
clk := quartz.NewMock(t)
|
||||
interceptionID := uuid.New()
|
||||
|
||||
middleware := NewBridgeMiddleware(tmpDir, "openai", "gpt-4", interceptionID, logger, clk)
|
||||
require.NotNil(t, middleware)
|
||||
|
||||
originalBody := `{"messages": [{"role": "user", "content": "hello"}]}`
|
||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, "https://api.openai.com/v1/chat/completions", bytes.NewReader([]byte(originalBody)))
|
||||
require.NoError(t, err)
|
||||
|
||||
var capturedBody []byte
|
||||
resp2, err := middleware(req, func(r *http.Request) (*http.Response, error) {
|
||||
// Read the body in the next handler to verify it's still available
|
||||
capturedBody, _ = io.ReadAll(r.Body)
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Status: "200 OK",
|
||||
Proto: "HTTP/1.1",
|
||||
Header: http.Header{},
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(`{}`))),
|
||||
}, nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer resp2.Body.Close()
|
||||
|
||||
// Verify the body was preserved for the next handler
|
||||
require.Equal(t, originalBody, string(capturedBody))
|
||||
}
|
||||
|
||||
func TestBridgedMiddleware_ModelWithSlash(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
|
||||
clk := quartz.NewMock(t)
|
||||
interceptionID := uuid.New()
|
||||
|
||||
// Model with slash should have it replaced with dash
|
||||
middleware := NewBridgeMiddleware(tmpDir, "google", "gemini/1.5-pro", interceptionID, logger, clk)
|
||||
require.NotNil(t, middleware)
|
||||
|
||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, "https://api.google.com/v1/chat", bytes.NewReader([]byte(`{}`)))
|
||||
require.NoError(t, err)
|
||||
|
||||
resp3, err := middleware(req, func(r *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Status: "200 OK",
|
||||
Proto: "HTTP/1.1",
|
||||
Header: http.Header{},
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(`{}`))),
|
||||
}, nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer resp3.Body.Close()
|
||||
|
||||
// Verify files are created with sanitized model name
|
||||
modelDir := filepath.Join(tmpDir, "google", "gemini-1.5-pro")
|
||||
reqDumpPath := findDumpFile(t, modelDir, SuffixRequest)
|
||||
_, err = os.Stat(reqDumpPath)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestPrettyPrintJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input []byte
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
input: []byte{},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "valid JSON",
|
||||
input: []byte(`{"key":"value"}`),
|
||||
expected: "{\n \"key\": \"value\"\n}\n",
|
||||
},
|
||||
{
|
||||
name: "invalid JSON returns as-is",
|
||||
input: []byte("not json"),
|
||||
expected: "not json",
|
||||
},
|
||||
// see: https://github.com/tidwall/pretty/blob/9090695766b652478676cc3e55bc3187056b1ff0/pretty.go#L117
|
||||
// for input starting with "t" it would change it to "true", eg. "t_rest_of_the_string_is_discarded" -> "true"
|
||||
// similar for inputs startrting with "f" and "n"
|
||||
{
|
||||
name: "invalid JSON edge case t",
|
||||
input: []byte("test"),
|
||||
expected: "test",
|
||||
},
|
||||
{
|
||||
name: "invalid JSON edge case f",
|
||||
input: []byte("f"),
|
||||
expected: "f",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result := prettyPrintJSON(tc.input)
|
||||
require.Equal(t, tc.expected, string(result))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBridgedMiddleware_AllSensitiveRequestHeaders(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
|
||||
clk := quartz.NewMock(t)
|
||||
interceptionID := uuid.New()
|
||||
|
||||
middleware := NewBridgeMiddleware(tmpDir, "openai", "gpt-4", interceptionID, logger, clk)
|
||||
require.NotNil(t, middleware)
|
||||
|
||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, "https://api.openai.com/v1/chat/completions", bytes.NewReader([]byte(`{}`)))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set all sensitive headers
|
||||
req.Header.Set("Authorization", "Bearer sk-secret-key")
|
||||
req.Header.Set("X-Api-Key", "secret-api-key")
|
||||
req.Header.Set("Api-Key", "another-secret")
|
||||
req.Header.Set("X-Auth-Token", "auth-token-val")
|
||||
req.Header.Set("Cookie", "session=abc123def")
|
||||
req.Header.Set("Proxy-Authorization", "Basic proxy-creds")
|
||||
req.Header.Set("X-Amz-Security-Token", "aws-security-token")
|
||||
|
||||
resp4, err := middleware(req, func(r *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Status: "200 OK",
|
||||
Proto: "HTTP/1.1",
|
||||
Header: http.Header{},
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(`{}`))),
|
||||
}, nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer resp4.Body.Close()
|
||||
|
||||
modelDir := filepath.Join(tmpDir, "openai", "gpt-4")
|
||||
reqDumpPath := findDumpFile(t, modelDir, SuffixRequest)
|
||||
reqContent, err := os.ReadFile(reqDumpPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
content := string(reqContent)
|
||||
|
||||
// Verify none of the full secret values are present
|
||||
require.NotContains(t, content, "sk-secret-key")
|
||||
require.NotContains(t, content, "secret-api-key")
|
||||
require.NotContains(t, content, "another-secret")
|
||||
require.NotContains(t, content, "auth-token-val")
|
||||
require.NotContains(t, content, "abc123def")
|
||||
require.NotContains(t, content, "proxy-creds")
|
||||
require.NotContains(t, content, "aws-security-token")
|
||||
require.NotContains(t, content, "google-api-key")
|
||||
|
||||
// But headers themselves are present (redacted)
|
||||
require.Contains(t, content, "Authorization:")
|
||||
require.Contains(t, content, "X-Api-Key:")
|
||||
require.Contains(t, content, "Api-Key:")
|
||||
require.Contains(t, content, "X-Auth-Token:")
|
||||
require.Contains(t, content, "Cookie:")
|
||||
require.Contains(t, content, "Proxy-Authorization:")
|
||||
require.Contains(t, content, "X-Amz-Security-Token:")
|
||||
}
|
||||
|
||||
func TestPassthroughMiddleware(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("empty_base_dir_returns_original_transport", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
|
||||
inner := http.DefaultTransport
|
||||
rt := NewPassthroughMiddleware(inner, "", "openai", logger, quartz.NewMock(t))
|
||||
require.Equal(t, inner, rt)
|
||||
})
|
||||
|
||||
t.Run("returns_error_from_inner_round_trip", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
|
||||
clk := quartz.NewMock(t)
|
||||
|
||||
innerErr := io.ErrUnexpectedEOF
|
||||
inner := &mockRoundTripper{
|
||||
roundTrip: func(_ *http.Request) (*http.Response, error) {
|
||||
return nil, innerErr
|
||||
},
|
||||
}
|
||||
|
||||
rt := NewPassthroughMiddleware(inner, tmpDir, "openai", logger, clk)
|
||||
|
||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "https://api.openai.com/v1/models", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err := rt.RoundTrip(req) //nolint:bodyclose // resp is nil on error
|
||||
require.ErrorIs(t, err, innerErr)
|
||||
require.Nil(t, resp)
|
||||
})
|
||||
|
||||
t.Run("dumps_request_and_response", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
|
||||
clk := quartz.NewMock(t)
|
||||
|
||||
req1Body := `first request`
|
||||
req2Body := `{"request": 2}`
|
||||
req2BodyPretty := "{\n \"request\": 2\n}\n"
|
||||
|
||||
callCount := 0
|
||||
inner := &mockRoundTripper{
|
||||
roundTrip: func(req *http.Request) (*http.Response, error) {
|
||||
// Verify body is still readable after dump
|
||||
body, err := io.ReadAll(req.Body)
|
||||
require.NoError(t, err)
|
||||
callCount++
|
||||
if callCount == 1 {
|
||||
require.Equal(t, req1Body, string(body))
|
||||
} else {
|
||||
require.Equal(t, req2Body, string(body))
|
||||
}
|
||||
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Status: "200 OK",
|
||||
Proto: "HTTP/1.1",
|
||||
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(fmt.Sprintf(`{"call": %d}"`, callCount)))),
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
rt := NewPassthroughMiddleware(inner, tmpDir, "openai", logger, clk)
|
||||
|
||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, "/v1/models", bytes.NewReader([]byte(req1Body)))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer sk-secret-key-12345")
|
||||
resp, err := rt.RoundTrip(req)
|
||||
require.NoError(t, err)
|
||||
_, err = io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
|
||||
// Second request should create new req/resp files
|
||||
req2, err := http.NewRequestWithContext(t.Context(), http.MethodPost, "/v1/conversations", bytes.NewReader([]byte(req2Body)))
|
||||
require.NoError(t, err)
|
||||
resp2, err := rt.RoundTrip(req2)
|
||||
require.NoError(t, err)
|
||||
_, err = io.ReadAll(resp2.Body)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp2.Body.Close())
|
||||
|
||||
// Validate request files contents
|
||||
passthroughDir := filepath.Join(tmpDir, "openai", "passthrough")
|
||||
req1Dump := readDumpFileContent(t, filepath.Join(passthroughDir, "*-v1-models-*"+SuffixRequest))
|
||||
req2Dump := readDumpFileContent(t, filepath.Join(passthroughDir, "*-v1-conversations-*"+SuffixRequest))
|
||||
|
||||
require.Contains(t, req1Dump, req1Body+"\n")
|
||||
require.Contains(t, req2Dump, req2BodyPretty)
|
||||
// Sensitive header should be redacted
|
||||
require.NotContains(t, req1Dump, "sk-secret-key-12345")
|
||||
require.NotContains(t, req2Dump, "sk-secret-key-12345")
|
||||
require.Contains(t, req1Dump, "Authorization:")
|
||||
require.NotContains(t, req2Dump, "Authorization:")
|
||||
|
||||
// Validate response files contents
|
||||
resp1Dump := readDumpFileContent(t, filepath.Join(passthroughDir, "*-v1-models-*"+SuffixResponse))
|
||||
resp2Dump := readDumpFileContent(t, filepath.Join(passthroughDir, "*-v1-conversations-*"+SuffixResponse))
|
||||
|
||||
require.Contains(t, resp1Dump, "200 OK")
|
||||
require.Contains(t, resp1Dump, `{"call": 1}"`)
|
||||
require.Contains(t, resp2Dump, "200 OK")
|
||||
require.Contains(t, resp2Dump, `{"call": 2}"`)
|
||||
})
|
||||
}
|
||||
|
||||
type mockRoundTripper struct {
|
||||
roundTrip func(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
func (m *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return m.roundTrip(req)
|
||||
}
|
||||
|
||||
// readDumpFileContent reads the content of the dump file matching the pattern.
|
||||
// Expects exactly one file to match the pattern.
|
||||
func readDumpFileContent(t *testing.T, pattern string) string {
|
||||
t.Helper()
|
||||
matches, err := filepath.Glob(pattern)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, matches, 1, "expected exactly one match got: %v %s", len(matches), strings.Join(matches, ", "), pattern)
|
||||
reqContent, readErr := os.ReadFile(matches[0])
|
||||
require.NoError(t, readErr)
|
||||
return string(reqContent)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package apidump
|
||||
|
||||
// sensitiveRequestHeaders are headers that should be redacted from request dumps.
|
||||
var sensitiveRequestHeaders = map[string]struct{}{
|
||||
"Authorization": {},
|
||||
"X-Api-Key": {},
|
||||
"Api-Key": {},
|
||||
"X-Auth-Token": {},
|
||||
"Cookie": {},
|
||||
"Proxy-Authorization": {},
|
||||
"X-Amz-Security-Token": {},
|
||||
}
|
||||
|
||||
// sensitiveResponseHeaders are headers that should be redacted from response dumps.
|
||||
// Note: header names use Go's canonical form (http.CanonicalHeaderKey).
|
||||
var sensitiveResponseHeaders = map[string]struct{}{
|
||||
"Set-Cookie": {},
|
||||
"Www-Authenticate": {},
|
||||
"Proxy-Authenticate": {},
|
||||
}
|
||||
|
||||
// redactHeaderValue redacts a sensitive header value, showing only partial content.
|
||||
// For values >= 8 bytes: shows first 4 and last 4 bytes with "..." in between.
|
||||
// For values < 8 bytes: shows first and last byte with "..." in between.
|
||||
func redactHeaderValue(value string) string {
|
||||
if len(value) >= 8 {
|
||||
return value[:4] + "..." + value[len(value)-4:]
|
||||
}
|
||||
if len(value) >= 2 {
|
||||
return value[:1] + "..." + value[len(value)-1:]
|
||||
}
|
||||
// Single character or empty - just return as-is
|
||||
return value
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package apidump //nolint:testpackage // tests unexported internals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cdr.dev/slog/v3"
|
||||
"github.com/coder/quartz"
|
||||
)
|
||||
|
||||
func TestRedactHeaderValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "empty string",
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "single char",
|
||||
input: "a",
|
||||
expected: "a",
|
||||
},
|
||||
{
|
||||
name: "two chars",
|
||||
input: "ab",
|
||||
expected: "a...b",
|
||||
},
|
||||
{
|
||||
name: "seven chars",
|
||||
input: "abcdefg",
|
||||
expected: "a...g",
|
||||
},
|
||||
{
|
||||
name: "eight chars - threshold",
|
||||
input: "abcdefgh",
|
||||
expected: "abcd...efgh",
|
||||
},
|
||||
{
|
||||
name: "long value",
|
||||
input: "Bearer sk-secret-key-12345",
|
||||
expected: "Bear...2345",
|
||||
},
|
||||
{
|
||||
name: "realistic api key",
|
||||
input: "sk-proj-abc123xyz789",
|
||||
expected: "sk-p...z789",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result := redactHeaderValue(tc.input)
|
||||
require.Equal(t, tc.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSensitiveHeaderLists(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Verify all expected sensitive request headers are in the list
|
||||
expectedRequestHeaders := []string{
|
||||
"Authorization",
|
||||
"X-Api-Key",
|
||||
"Api-Key",
|
||||
"X-Auth-Token",
|
||||
"Cookie",
|
||||
"Proxy-Authorization",
|
||||
"X-Amz-Security-Token",
|
||||
}
|
||||
for _, h := range expectedRequestHeaders {
|
||||
_, ok := sensitiveRequestHeaders[h]
|
||||
require.True(t, ok, "expected %q to be in sensitiveRequestHeaders", h)
|
||||
}
|
||||
|
||||
// Verify all expected sensitive response headers are in the list
|
||||
// Note: header names use Go's canonical form (http.CanonicalHeaderKey)
|
||||
expectedResponseHeaders := []string{
|
||||
"Set-Cookie",
|
||||
"Www-Authenticate",
|
||||
"Proxy-Authenticate",
|
||||
}
|
||||
for _, h := range expectedResponseHeaders {
|
||||
_, ok := sensitiveResponseHeaders[h]
|
||||
require.True(t, ok, "expected %q to be in sensitiveResponseHeaders", h)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteRedactedHeaders(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
d := &dumper{
|
||||
dumpPath: interceptDumpPath("/tmp", "test", "test", uuid.New(), quartz.NewMock(t)),
|
||||
logger: slog.Make(),
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
headers http.Header
|
||||
sensitive map[string]struct{}
|
||||
overrides map[string]string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "empty headers",
|
||||
headers: http.Header{},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "single header",
|
||||
headers: http.Header{"Content-Type": {"application/json"}},
|
||||
expected: "Content-Type: application/json\r\n",
|
||||
},
|
||||
{
|
||||
name: "sorted alphabetically",
|
||||
headers: http.Header{
|
||||
"Zebra": {"last"},
|
||||
"Alpha": {"first"},
|
||||
},
|
||||
expected: "Alpha: first\r\nZebra: last\r\n",
|
||||
},
|
||||
{
|
||||
name: "override applied",
|
||||
headers: http.Header{"Content-Length": {"100"}},
|
||||
overrides: map[string]string{"Content-Length": "200"},
|
||||
expected: "Content-Length: 200\r\n",
|
||||
},
|
||||
{
|
||||
name: "sensitive header redacted",
|
||||
headers: http.Header{"Set-Cookie": {"session=abcdefghij"}},
|
||||
sensitive: sensitiveResponseHeaders,
|
||||
expected: "Set-Cookie: sess...ghij\r\n",
|
||||
},
|
||||
{
|
||||
name: "multi-value header",
|
||||
headers: http.Header{
|
||||
"Accept": {"text/html", "application/json"},
|
||||
},
|
||||
expected: "Accept: text/html\r\nAccept: application/json\r\n",
|
||||
},
|
||||
{
|
||||
name: "override for non-existent header",
|
||||
headers: http.Header{},
|
||||
overrides: map[string]string{"Host": "example.com"},
|
||||
expected: "Host: example.com\r\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var buf bytes.Buffer
|
||||
d.writeRedactedHeaders(&buf, tc.headers, tc.sensitive, tc.overrides)
|
||||
require.Equal(t, tc.expected, buf.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package apidump
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// streamingBodyDumper wraps an io.ReadCloser and writes all data to a dump file
|
||||
// as it's read, preserving streaming behavior.
|
||||
type streamingBodyDumper struct {
|
||||
body io.ReadCloser
|
||||
dumpPath string
|
||||
headerData []byte
|
||||
logger func(err error)
|
||||
|
||||
once sync.Once
|
||||
file *os.File
|
||||
initErr error
|
||||
}
|
||||
|
||||
func (s *streamingBodyDumper) init() {
|
||||
s.once.Do(func() {
|
||||
if err := os.MkdirAll(filepath.Dir(s.dumpPath), 0o755); err != nil {
|
||||
s.initErr = xerrors.Errorf("create dump dir: %w", err)
|
||||
return
|
||||
}
|
||||
f, err := os.Create(s.dumpPath)
|
||||
if err != nil {
|
||||
s.initErr = xerrors.Errorf("create dump file: %w", err)
|
||||
return
|
||||
}
|
||||
s.file = f
|
||||
// Write headers first.
|
||||
if _, err := s.file.Write(s.headerData); err != nil {
|
||||
s.initErr = xerrors.Errorf("write headers: %w", err)
|
||||
_ = s.file.Close() // best-effort cleanup on header write failure
|
||||
s.file = nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *streamingBodyDumper) Read(p []byte) (int, error) {
|
||||
n, err := s.body.Read(p)
|
||||
if n > 0 {
|
||||
s.init()
|
||||
if s.initErr != nil && s.logger != nil {
|
||||
s.logger(s.initErr)
|
||||
}
|
||||
if s.file != nil {
|
||||
// Write raw bytes as they stream through.
|
||||
_, _ = s.file.Write(p[:n])
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (s *streamingBodyDumper) Close() error {
|
||||
// Ensure init() has completed to avoid racing with Read().
|
||||
s.init()
|
||||
var closeErr error
|
||||
if s.file != nil {
|
||||
closeErr = s.file.Close()
|
||||
}
|
||||
bodyErr := s.body.Close()
|
||||
if bodyErr != nil {
|
||||
return bodyErr
|
||||
}
|
||||
return closeErr
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package apidump //nolint:testpackage // shares test helpers with apidump_test.go
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cdr.dev/slog/v3"
|
||||
"cdr.dev/slog/v3/sloggers/slogtest"
|
||||
"github.com/coder/quartz"
|
||||
)
|
||||
|
||||
func TestMiddleware_StreamingResponse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
|
||||
clk := quartz.NewMock(t)
|
||||
interceptionID := uuid.New()
|
||||
|
||||
middleware := NewBridgeMiddleware(tmpDir, "openai", "gpt-4", interceptionID, logger, clk)
|
||||
require.NotNil(t, middleware)
|
||||
|
||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, "https://api.openai.com/v1/chat/completions", bytes.NewReader([]byte(`{}`)))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Simulate a streaming response with multiple chunks
|
||||
chunks := []string{
|
||||
"data: {\"chunk\": 1}\n\n",
|
||||
"data: {\"chunk\": 2}\n\n",
|
||||
"data: {\"chunk\": 3}\n\n",
|
||||
"data: [DONE]\n\n",
|
||||
}
|
||||
|
||||
// Create a pipe to simulate streaming
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
defer pw.Close() //nolint:revive // error handled via pipe read side
|
||||
for _, chunk := range chunks {
|
||||
if _, err := pw.Write([]byte(chunk)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
resp, err := middleware(req, func(r *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Status: "200 OK",
|
||||
Proto: "HTTP/1.1",
|
||||
Header: http.Header{"Content-Type": []string{"text/event-stream"}},
|
||||
Body: pr,
|
||||
}, nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Read response in small chunks to simulate streaming consumption
|
||||
var receivedData bytes.Buffer
|
||||
buf := make([]byte, 16)
|
||||
for {
|
||||
n, err := resp.Body.Read(buf)
|
||||
if n > 0 {
|
||||
_, _ = receivedData.Write(buf[:n]) // bytes.Buffer.Write never fails
|
||||
}
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, resp.Body.Close())
|
||||
|
||||
// Verify we received all the data
|
||||
expectedData := strings.Join(chunks, "")
|
||||
require.Equal(t, expectedData, receivedData.String())
|
||||
|
||||
// Verify the dump file was created and contains all the streamed data
|
||||
modelDir := filepath.Join(tmpDir, "openai", "gpt-4")
|
||||
respDumpPath := findDumpFile(t, modelDir, SuffixResponse)
|
||||
respContent, err := os.ReadFile(respDumpPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
content := string(respContent)
|
||||
require.Contains(t, content, "HTTP/1.1 200 OK")
|
||||
require.Contains(t, content, "Content-Type: text/event-stream")
|
||||
// All chunks should be in the dump
|
||||
for _, chunk := range chunks {
|
||||
require.Contains(t, content, chunk)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMiddleware_PreservesResponseBody(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: false}).Leveled(slog.LevelDebug)
|
||||
clk := quartz.NewMock(t)
|
||||
interceptionID := uuid.New()
|
||||
|
||||
middleware := NewBridgeMiddleware(tmpDir, "openai", "gpt-4", interceptionID, logger, clk)
|
||||
require.NotNil(t, middleware)
|
||||
|
||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, "https://api.openai.com/v1/chat/completions", bytes.NewReader([]byte(`{}`)))
|
||||
require.NoError(t, err)
|
||||
|
||||
originalRespBody := `{"choices": [{"message": {"content": "hi"}}]}`
|
||||
resp, err := middleware(req, func(r *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Status: "200 OK",
|
||||
Proto: "HTTP/1.1",
|
||||
Header: http.Header{},
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(originalRespBody))),
|
||||
}, nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Verify the response body is still readable after middleware
|
||||
capturedBody, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, originalRespBody, string(capturedBody))
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package chatcompletions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/openai/openai-go/v3"
|
||||
"github.com/openai/openai-go/v3/option"
|
||||
"github.com/openai/openai-go/v3/shared"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"cdr.dev/slog/v3"
|
||||
"github.com/coder/coder/v2/aibridge/config"
|
||||
aibcontext "github.com/coder/coder/v2/aibridge/context"
|
||||
"github.com/coder/coder/v2/aibridge/intercept"
|
||||
"github.com/coder/coder/v2/aibridge/intercept/apidump"
|
||||
"github.com/coder/coder/v2/aibridge/mcp"
|
||||
"github.com/coder/coder/v2/aibridge/recorder"
|
||||
"github.com/coder/coder/v2/aibridge/tracing"
|
||||
"github.com/coder/quartz"
|
||||
)
|
||||
|
||||
type interceptionBase struct {
|
||||
id uuid.UUID
|
||||
providerName string
|
||||
req *ChatCompletionNewParamsWrapper
|
||||
cfg config.OpenAI
|
||||
|
||||
// clientHeaders are the original HTTP headers from the client request.
|
||||
clientHeaders http.Header
|
||||
authHeaderName string
|
||||
|
||||
logger slog.Logger
|
||||
tracer trace.Tracer
|
||||
|
||||
recorder recorder.Recorder
|
||||
mcpProxy mcp.ServerProxier
|
||||
credential intercept.CredentialInfo
|
||||
}
|
||||
|
||||
func (i *interceptionBase) newCompletionsService() openai.ChatCompletionService {
|
||||
opts := []option.RequestOption{option.WithAPIKey(i.cfg.Key), option.WithBaseURL(i.cfg.BaseURL)}
|
||||
|
||||
// Add extra headers if configured.
|
||||
// Some providers require additional headers that are not added by the SDK.
|
||||
// TODO(ssncferreira): remove as part of https://github.com/coder/aibridge/issues/192
|
||||
for key, value := range i.cfg.ExtraHeaders {
|
||||
opts = append(opts, option.WithHeader(key, value))
|
||||
}
|
||||
|
||||
// Forward client headers to upstream. This middleware runs after the SDK
|
||||
// has built the request, and replaces the outgoing headers with the sanitized
|
||||
// client headers plus provider auth.
|
||||
if i.clientHeaders != nil {
|
||||
opts = append(opts, option.WithMiddleware(func(req *http.Request, next option.MiddlewareNext) (*http.Response, error) {
|
||||
req.Header = intercept.BuildUpstreamHeaders(req.Header, i.clientHeaders, i.authHeaderName)
|
||||
return next(req)
|
||||
}))
|
||||
}
|
||||
|
||||
// Add API dump middleware if configured
|
||||
if mw := apidump.NewBridgeMiddleware(i.cfg.APIDumpDir, i.providerName, i.Model(), i.id, i.logger, quartz.NewReal()); mw != nil {
|
||||
opts = append(opts, option.WithMiddleware(mw))
|
||||
}
|
||||
|
||||
return openai.NewChatCompletionService(opts...)
|
||||
}
|
||||
|
||||
func (i *interceptionBase) ID() uuid.UUID {
|
||||
return i.id
|
||||
}
|
||||
|
||||
func (i *interceptionBase) Credential() intercept.CredentialInfo {
|
||||
return i.credential
|
||||
}
|
||||
|
||||
func (i *interceptionBase) Setup(logger slog.Logger, rec recorder.Recorder, mcpProxy mcp.ServerProxier) {
|
||||
i.logger = logger
|
||||
i.recorder = rec
|
||||
i.mcpProxy = mcpProxy
|
||||
}
|
||||
|
||||
func (i *interceptionBase) CorrelatingToolCallID() *string {
|
||||
if len(i.req.Messages) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The tool result should be the last input message.
|
||||
msg := i.req.Messages[len(i.req.Messages)-1]
|
||||
if msg.OfTool == nil {
|
||||
return nil
|
||||
}
|
||||
return &msg.OfTool.ToolCallID
|
||||
}
|
||||
|
||||
func (i *interceptionBase) baseTraceAttributes(r *http.Request, streaming bool) []attribute.KeyValue {
|
||||
return []attribute.KeyValue{
|
||||
attribute.String(tracing.RequestPath, r.URL.Path),
|
||||
attribute.String(tracing.InterceptionID, i.id.String()),
|
||||
attribute.String(tracing.InitiatorID, aibcontext.ActorIDFromContext(r.Context())),
|
||||
attribute.String(tracing.Provider, i.providerName),
|
||||
attribute.String(tracing.Model, i.Model()),
|
||||
attribute.Bool(tracing.Streaming, streaming),
|
||||
}
|
||||
}
|
||||
|
||||
func (i *interceptionBase) Model() string {
|
||||
if i.req == nil {
|
||||
return "coder-aibridge-unknown"
|
||||
}
|
||||
|
||||
return i.req.Model
|
||||
}
|
||||
|
||||
func (*interceptionBase) newErrorResponse(err error) map[string]any {
|
||||
return map[string]any{
|
||||
"error": true,
|
||||
"message": err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
func (i *interceptionBase) injectTools() {
|
||||
if i.req == nil || i.mcpProxy == nil || !i.hasInjectableTools() {
|
||||
return
|
||||
}
|
||||
|
||||
// Disable parallel tool calls when injectable tools are present to simplify the inner agentic loop.
|
||||
i.req.ParallelToolCalls = openai.Bool(false)
|
||||
|
||||
// Inject tools.
|
||||
for _, tool := range i.mcpProxy.ListTools() {
|
||||
fn := openai.ChatCompletionToolUnionParam{
|
||||
OfFunction: &openai.ChatCompletionFunctionToolParam{
|
||||
Function: openai.FunctionDefinitionParam{
|
||||
Name: tool.ID,
|
||||
Strict: openai.Bool(false), // TODO: configurable.
|
||||
Description: openai.String(tool.Description),
|
||||
Parameters: openai.FunctionParameters{
|
||||
"type": "object",
|
||||
"properties": tool.Params,
|
||||
// "additionalProperties": false, // Only relevant when strict=true.
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Otherwise the request fails with "None is not of type 'array'" if a nil slice is given.
|
||||
if len(tool.Required) > 0 {
|
||||
// Must list ALL properties when strict=true.
|
||||
fn.OfFunction.Function.Parameters["required"] = tool.Required
|
||||
}
|
||||
|
||||
i.req.Tools = append(i.req.Tools, fn)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *interceptionBase) unmarshalArgs(in string) (args recorder.ToolArgs) {
|
||||
if len(strings.TrimSpace(in)) == 0 {
|
||||
return args // An empty string will fail JSON unmarshaling.
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(in), &args); err != nil {
|
||||
i.logger.Warn(context.Background(), "failed to unmarshal tool args", slog.Error(err))
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
// writeUpstreamError marshals and writes a given error.
|
||||
func (i *interceptionBase) writeUpstreamError(w http.ResponseWriter, oaiErr *chatCompletionResponseError) {
|
||||
if oaiErr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(oaiErr.StatusCode)
|
||||
|
||||
out, err := json.Marshal(oaiErr)
|
||||
if err != nil {
|
||||
i.logger.Warn(context.Background(), "failed to marshal upstream error", slog.Error(err), slog.F("error_payload", oaiErr))
|
||||
// Response has to match expected format.
|
||||
_, _ = w.Write([]byte(`{
|
||||
"error": {
|
||||
"type": "error",
|
||||
"message":"error marshaling upstream error",
|
||||
"code": "server_error"
|
||||
},
|
||||
}`))
|
||||
} else {
|
||||
_, _ = w.Write(out)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *interceptionBase) hasInjectableTools() bool {
|
||||
return i.mcpProxy != nil && len(i.mcpProxy.ListTools()) > 0
|
||||
}
|
||||
|
||||
func sumUsage(ref, in openai.CompletionUsage) openai.CompletionUsage {
|
||||
return openai.CompletionUsage{
|
||||
CompletionTokens: ref.CompletionTokens + in.CompletionTokens,
|
||||
PromptTokens: ref.PromptTokens + in.PromptTokens,
|
||||
TotalTokens: ref.TotalTokens + in.TotalTokens,
|
||||
CompletionTokensDetails: openai.CompletionUsageCompletionTokensDetails{
|
||||
AcceptedPredictionTokens: ref.CompletionTokensDetails.AcceptedPredictionTokens + in.CompletionTokensDetails.AcceptedPredictionTokens,
|
||||
AudioTokens: ref.CompletionTokensDetails.AudioTokens + in.CompletionTokensDetails.AudioTokens,
|
||||
ReasoningTokens: ref.CompletionTokensDetails.ReasoningTokens + in.CompletionTokensDetails.ReasoningTokens,
|
||||
RejectedPredictionTokens: ref.CompletionTokensDetails.RejectedPredictionTokens + in.CompletionTokensDetails.RejectedPredictionTokens,
|
||||
},
|
||||
PromptTokensDetails: openai.CompletionUsagePromptTokensDetails{
|
||||
AudioTokens: ref.PromptTokensDetails.AudioTokens + in.PromptTokensDetails.AudioTokens,
|
||||
CachedTokens: ref.PromptTokensDetails.CachedTokens + in.PromptTokensDetails.CachedTokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// calculateActualInputTokenUsage accounts for cached tokens which are included in [openai.CompletionUsage].PromptTokens.
|
||||
func calculateActualInputTokenUsage(in openai.CompletionUsage) int64 {
|
||||
// Input *includes* the cached tokens, so we subtract them here to reflect actual input token usage.
|
||||
// The original value can be reconstructed by adding CachedTokens back to Input.
|
||||
// See https://platform.openai.com/docs/api-reference/usage/completions_object#usage/completions_object-input_tokens.
|
||||
return in.PromptTokens /* The aggregated number of text input tokens used, including cached tokens. */ -
|
||||
in.PromptTokensDetails.CachedTokens /* The aggregated number of text input tokens that has been cached from previous requests. */
|
||||
}
|
||||
|
||||
func getErrorResponse(err error) *chatCompletionResponseError {
|
||||
var apiErr *openai.Error
|
||||
if !errors.As(err, &apiErr) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &chatCompletionResponseError{
|
||||
ErrorObject: &shared.ErrorObject{
|
||||
Code: apiErr.Code,
|
||||
Message: apiErr.Message,
|
||||
Type: apiErr.Type,
|
||||
},
|
||||
StatusCode: apiErr.StatusCode,
|
||||
}
|
||||
}
|
||||
|
||||
var _ error = &chatCompletionResponseError{}
|
||||
|
||||
type chatCompletionResponseError struct {
|
||||
ErrorObject *shared.ErrorObject `json:"error"`
|
||||
StatusCode int `json:"-"`
|
||||
}
|
||||
|
||||
func newErrorResponse(msg error) *chatCompletionResponseError {
|
||||
return &chatCompletionResponseError{
|
||||
ErrorObject: &shared.ErrorObject{
|
||||
Code: "error",
|
||||
Message: msg.Error(),
|
||||
Type: "error",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (a *chatCompletionResponseError) Error() string {
|
||||
if a.ErrorObject == nil {
|
||||
return ""
|
||||
}
|
||||
return a.ErrorObject.Message
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user