Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.8.0] - 2025-01-31
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix the release date.

The changelog shows release date as 2025-01-31, but this PR was created on 2025-11-25. Please update to the correct release date or use Unreleased if the version hasn't been released yet.

Apply this diff to use standard unreleased format:

-## [0.8.0] - 2025-01-31
+## [Unreleased]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## [0.8.0] - 2025-01-31
## [Unreleased]
🤖 Prompt for AI Agents
In CHANGELOG.md around line 8, the release date for version 0.8.0 is incorrect
(shows 2025-01-31) — update that line to either the correct release date
(2025-11-25) or change the section header to use the standard "Unreleased"
format (e.g., "## [0.8.0] - Unreleased") depending on whether the release is
published; ensure the header matches the project's changelog style.


### Added
- GitHub Enterprise Server/Cloud support
- New `--enterprise-url` flag for `auth` and `start` commands
- Interactive prompt during auth for enterprise configuration
- Persistent enterprise URL storage in `~/.local/share/copilot-api/enterprise_url`
- Automatic endpoint routing for enterprise instances
- Comprehensive test suite with 41 new tests covering enterprise functionality
- URL normalization utilities for enterprise host configuration

### Changed
- OAuth endpoints now use enterprise URLs when configured
- Copilot API endpoints route to `copilot-api.{enterprise}` for enterprise users
- GitHub API endpoints route to `api.{enterprise}` for enterprise users

### Technical Details
- Enterprise endpoint structure:
- OAuth: `https://{enterprise}/login/...`
- GitHub API: `https://api.{enterprise}/...`
- Copilot API: `https://copilot-api.{enterprise}/...`
- 100% backwards compatible - defaults to github.com when no enterprise configured

## [0.7.0] - Previous Release
- See git history for details
41 changes: 41 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# CLAUDE.md

This file provides guidance to Claude Code when working with this repository.

## Overview

TypeScript CLI application using Bun runtime that exposes GitHub Copilot as OpenAI/Anthropic-compatible APIs.

## Commands

- `bun install` - Install dependencies
- `bun run build` - Build distribution
- `bun run dev` - Development with watch mode
- `bun run start` - Production start
- `bun run lint` - Lint code
- `bun run typecheck` - Type checking
- `bun test` - Run tests

## Architecture

- **Entry**: `src/main.ts` - CLI definition with subcommands (auth, start, check-usage, debug)
- **Server**: `src/start.ts` - Orchestrates initialization and HTTP server
- **Routes**: `src/routes/` - OpenAI & Anthropic compatible endpoints
- **Services**:
- `src/services/github/` - GitHub OAuth and token management
- `src/services/copilot/` - Copilot API interactions
- **Utilities**: `src/lib/` - State management, paths, token handling

## GitHub Enterprise Support

- Use `--enterprise-url` flag for GHE Server/Cloud
- Interactive prompt during auth if no flag provided
- Enterprise URL persisted in `~/.local/share/copilot-api/enterprise_url`
- Endpoints: `https://{enterprise}/...` for OAuth, `https://api.{enterprise}/...` for GitHub API, `https://copilot-api.{enterprise}/...` for Copilot

## Key Files

- CLI flags: `src/start.ts:123`, `src/main.ts:10`
- Routes: `src/server.ts`, `src/routes/*`
- Models: `src/services/copilot/get-models.ts`
- Token storage: `src/lib/paths.ts`, `src/lib/token.ts`
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ A reverse-engineered proxy for the GitHub Copilot API that exposes it as an Open
- **Token Visibility**: Option to display GitHub and Copilot tokens during authentication and refresh for debugging (`--show-token`).
- **Flexible Authentication**: Authenticate interactively or provide a GitHub token directly, suitable for CI/CD environments.
- **Support for Different Account Types**: Works with individual, business, and enterprise GitHub Copilot plans.
- **GitHub Enterprise Support**: Compatible with GitHub Enterprise Server and GitHub Enterprise Cloud installations.

## Demo

Expand Down Expand Up @@ -163,13 +164,15 @@ The following command line options are available for the `start` command:
| --claude-code | Generate a command to launch Claude Code with Copilot API config | false | -c |
| --show-token | Show GitHub and Copilot tokens on fetch and refresh | false | none |
| --proxy-env | Initialize proxy from environment variables | false | none |
| --enterprise-url | GitHub Enterprise host to use (eg. https://ghe.example.com) | none | none |

### Auth Command Options

| Option | Description | Default | Alias |
| ------------ | ------------------------- | ------- | ----- |
| --verbose | Enable verbose logging | false | -v |
| --show-token | Show GitHub token on auth | false | none |
| --enterprise-url | GitHub Enterprise host (eg. https://ghe.example.com) | none | none |

### Debug Command Options

Expand Down Expand Up @@ -255,6 +258,15 @@ npx copilot-api@latest debug --json

# Initialize proxy from environment variables (HTTP_PROXY, HTTPS_PROXY, etc.)
npx copilot-api@latest start --proxy-env

# Use GitHub Enterprise / GitHub Enterprise Server
npx copilot-api@latest start --account-type enterprise --enterprise-url https://ghe.example.com

# Authenticate with GitHub Enterprise interactively (will prompt for enterprise host)
npx copilot-api@latest auth

# Authenticate with GitHub Enterprise using CLI flag (for scripting)
npx copilot-api@latest auth --enterprise-url ghe.example.com
```

## Using the Usage Viewer
Expand Down Expand Up @@ -349,3 +361,8 @@ bun run start
- `--rate-limit <seconds>`: Enforces a minimum time interval between requests. For example, `copilot-api start --rate-limit 30` will ensure there's at least a 30-second gap between requests.
- `--wait`: Use this with `--rate-limit`. It makes the server wait for the cooldown period to end instead of rejecting the request with an error. This is useful for clients that don't automatically retry on rate limit errors.
- If you have a GitHub business or enterprise plan account with Copilot, use the `--account-type` flag (e.g., `--account-type business`). See the [official documentation](https://docs.github.com/en/enterprise-cloud@latest/copilot/managing-copilot/managing-github-copilot-in-your-organization/managing-access-to-github-copilot-in-your-organization/managing-github-copilot-access-to-your-organizations-network#configuring-copilot-subscription-based-network-routing-for-your-enterprise-or-organization) for more details.
- For GitHub Enterprise Server/Cloud users: Use `--enterprise-url` to specify your enterprise host (e.g., `--enterprise-url https://ghe.example.com`). The interactive auth command (`copilot-api auth`) will prompt you for your enterprise host if you don't provide it via the CLI flag.



export ANTHROPIC_BASE_URL=http://localhost:4141 ANTHROPIC_AUTH_TOKEN=dummy ANTHROPIC_MODEL=claude-sonnet-4.5 ANTHROPIC_DEFAULT_SONNET_MODEL=claude-sonnet-4.5 ANTHROPIC_SMALL_FAST_MODEL=gpt-5-mini ANTHROPIC_DEFAULT_HAIKU_MODEL=gpt-5-mini DISABLE_NON_ESSENTIAL_MODEL_CALLS=1 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 && claude
Comment on lines +364 to +368
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix malformed export command example.

Lines 365-368 appear to be improperly formatted. The export command is split across lines without proper markdown code block formatting, making it difficult to read and copy.

Apply this diff to properly format the export command:

 - For GitHub Enterprise Server/Cloud users: Use `--enterprise-url` to specify your enterprise host (e.g., `--enterprise-url https://ghe.example.com`). The interactive auth command (`copilot-api auth`) will prompt you for your enterprise host if you don't provide it via the CLI flag.
-
-
-
-export ANTHROPIC_BASE_URL=http://localhost:4141 ANTHROPIC_AUTH_TOKEN=dummy ANTHROPIC_MODEL=claude-sonnet-4.5 ANTHROPIC_DEFAULT_SONNET_MODEL=claude-sonnet-4.5 ANTHROPIC_SMALL_FAST_MODEL=gpt-5-mini ANTHROPIC_DEFAULT_HAIKU_MODEL=gpt-5-mini DISABLE_NON_ESSENTIAL_MODEL_CALLS=1 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 && claude
+
+Example command to launch Claude with Copilot API:
+
+```sh
+export ANTHROPIC_BASE_URL=http://localhost:4141 \
+  ANTHROPIC_AUTH_TOKEN=dummy \
+  ANTHROPIC_MODEL=claude-sonnet-4.5 \
+  ANTHROPIC_DEFAULT_SONNET_MODEL=claude-sonnet-4.5 \
+  ANTHROPIC_SMALL_FAST_MODEL=gpt-5-mini \
+  ANTHROPIC_DEFAULT_HAIKU_MODEL=gpt-5-mini \
+  DISABLE_NON_ESSENTIAL_MODEL_CALLS=1 \
+  CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 && claude
+```
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

368-368: Bare URL used

(MD034, no-bare-urls)

🤖 Prompt for AI Agents
In README.md around lines 364 to 368 the example export command is malformed and
split across lines without a fenced code block; wrap the environment-variable
block in a proper fenced code block (```sh), place the export on one logical
command using backslashes for line continuations so each ANTHROPIC_* and flag is
on its own indented line, and add the closing ``` to restore correct formatting
and copy-pastability.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "copilot-api",
"version": "0.7.0",
"version": "0.8.0",
"description": "Turn GitHub Copilot into OpenAI/Anthropic API compatible server. Usable with Claude Code!",
"keywords": [
"proxy",
Expand Down
35 changes: 33 additions & 2 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { setupGitHubToken } from "./lib/token"
interface RunAuthOptions {
verbose: boolean
showToken: boolean
enterpriseUrl?: string
}

export async function runAuth(options: RunAuthOptions): Promise<void> {
Expand All @@ -19,9 +20,33 @@ export async function runAuth(options: RunAuthOptions): Promise<void> {
}

state.showToken = options.showToken

await ensurePaths()
await setupGitHubToken({ force: true })

// If no enterpriseUrl provided, ask interactively whether the user uses GH Enterprise.
let enterprise = options.enterpriseUrl
if (!enterprise) {
const resp = await consola.prompt(
"Are you using GitHub Enterprise / GitHub Enterprise Server?",
{
type: "confirm",
initial: false,
},
)
if (resp) {
const hostResp = await consola.prompt(
"Enter enterprise host (eg. ghe.example.com or https://ghe.example.com):",
{
type: "text",
},
)
enterprise = hostResp
}
}

await setupGitHubToken({
force: true,
...(enterprise ? { enterpriseUrl: enterprise } : {}),
})
consola.success("GitHub token written to", PATHS.GITHUB_TOKEN_PATH)
}

Expand All @@ -42,11 +67,17 @@ export const auth = defineCommand({
default: false,
description: "Show GitHub token on auth",
},
"enterprise-url": {
type: "string",
description:
"GitHub Enterprise host (eg. https://ghe.example.com or ghe.example.com)",
},
},
run({ args }) {
return runAuth({
verbose: args.verbose,
showToken: args["show-token"],
enterpriseUrl: args["enterprise-url"],
})
},
})
22 changes: 16 additions & 6 deletions src/lib/api-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { randomUUID } from "node:crypto"

import type { State } from "./state"

import { state } from "./state"
import { githubBaseUrl, githubApiBaseUrl } from "./url"

export const standardHeaders = () => ({
"content-type": "application/json",
accept: "application/json",
Expand All @@ -13,10 +16,17 @@ const USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`

const API_VERSION = "2025-04-01"

export const copilotBaseUrl = (state: State) =>
state.accountType === "individual" ?
"https://api.githubcopilot.com"
: `https://api.${state.accountType}.githubcopilot.com`
export const copilotBaseUrl = (state: State) => {
// If enterprise URL is configured, use enterprise Copilot API endpoint
if (state.enterpriseUrl) {
return `https://copilot-api.${state.enterpriseUrl}`
}

// Otherwise use standard GitHub Copilot endpoints based on account type
return state.accountType === "individual" ?
"https://api.githubcopilot.com"
: `https://api.${state.accountType}.githubcopilot.com`
}
Comment on lines +19 to +29
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Enterprise URL not normalized in copilotBaseUrl.

The copilotBaseUrl function directly interpolates state.enterpriseUrl without normalization. If enterpriseUrl contains a protocol prefix (e.g., https://ghe.example.com), the resulting URL will be malformed: https://copilot-api.https://ghe.example.com.

Other functions use githubBaseUrl and githubApiBaseUrl from ./url.ts which call normalizeDomain. Consider using the same normalization here.

+import { normalizeDomain, githubBaseUrl, githubApiBaseUrl } from "./url"
+
 export const copilotBaseUrl = (state: State) => {
   // If enterprise URL is configured, use enterprise Copilot API endpoint
   if (state.enterpriseUrl) {
-    return `https://copilot-api.${state.enterpriseUrl}`
+    const domain = normalizeDomain(state.enterpriseUrl)
+    return `https://copilot-api.${domain}`
   }

   // Otherwise use standard GitHub Copilot endpoints based on account type
   return state.accountType === "individual" ?
       "https://api.githubcopilot.com"
     : `https://api.${state.accountType}.githubcopilot.com`
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const copilotBaseUrl = (state: State) => {
// If enterprise URL is configured, use enterprise Copilot API endpoint
if (state.enterpriseUrl) {
return `https://copilot-api.${state.enterpriseUrl}`
}
// Otherwise use standard GitHub Copilot endpoints based on account type
return state.accountType === "individual" ?
"https://api.githubcopilot.com"
: `https://api.${state.accountType}.githubcopilot.com`
}
import { normalizeDomain, githubBaseUrl, githubApiBaseUrl } from "./url"
export const copilotBaseUrl = (state: State) => {
// If enterprise URL is configured, use enterprise Copilot API endpoint
if (state.enterpriseUrl) {
const domain = normalizeDomain(state.enterpriseUrl)
return `https://copilot-api.${domain}`
}
// Otherwise use standard GitHub Copilot endpoints based on account type
return state.accountType === "individual" ?
"https://api.githubcopilot.com"
: `https://api.${state.accountType}.githubcopilot.com`
}
🤖 Prompt for AI Agents
In src/lib/api-config.ts around lines 19 to 29, the function interpolates
state.enterpriseUrl directly which can include a protocol and produce malformed
URLs; update the function to import and call normalizeDomain (from ./url.ts) on
state.enterpriseUrl (or otherwise strip any protocol/trailing slashes) and use
the normalized domain when building the copilot API URL (e.g., const domain =
normalizeDomain(state.enterpriseUrl); return `https://copilot-api.${domain}`) so
the returned URL is always well-formed.

export const copilotHeaders = (state: State, vision: boolean = false) => {
const headers: Record<string, string> = {
Authorization: `Bearer ${state.copilotToken}`,
Expand All @@ -36,7 +46,7 @@ export const copilotHeaders = (state: State, vision: boolean = false) => {
return headers
}

export const GITHUB_API_BASE_URL = "https://api.github.com"
export const GITHUB_API_BASE_URL = () => githubApiBaseUrl(state.enterpriseUrl)
export const githubHeaders = (state: State) => ({
...standardHeaders(),
authorization: `token ${state.githubToken}`,
Expand All @@ -47,6 +57,6 @@ export const githubHeaders = (state: State) => ({
"x-vscode-user-agent-library-version": "electron-fetch",
})

export const GITHUB_BASE_URL = "https://github.com"
export const GITHUB_BASE_URL = () => githubBaseUrl(state.enterpriseUrl)
export const GITHUB_CLIENT_ID = "Iv1.b507a08c87ecfe98"
export const GITHUB_APP_SCOPES = ["read:user"].join(" ")
3 changes: 3 additions & 0 deletions src/lib/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ import path from "node:path"
const APP_DIR = path.join(os.homedir(), ".local", "share", "copilot-api")

const GITHUB_TOKEN_PATH = path.join(APP_DIR, "github_token")
const ENTERPRISE_URL_PATH = path.join(APP_DIR, "enterprise_url")

export const PATHS = {
APP_DIR,
GITHUB_TOKEN_PATH,
ENTERPRISE_URL_PATH,
}

export async function ensurePaths(): Promise<void> {
await fs.mkdir(PATHS.APP_DIR, { recursive: true })
await ensureFile(PATHS.GITHUB_TOKEN_PATH)
await ensureFile(PATHS.ENTERPRISE_URL_PATH)
}

async function ensureFile(filePath: string): Promise<void> {
Expand Down
2 changes: 2 additions & 0 deletions src/lib/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { ModelsResponse } from "~/services/copilot/get-models"
export interface State {
githubToken?: string
copilotToken?: string
enterpriseUrl?: string

accountType: string
models?: ModelsResponse
Expand All @@ -22,4 +23,5 @@ export const state: State = {
manualApprove: false,
rateLimitWait: false,
showToken: false,
enterpriseUrl: undefined,
}
23 changes: 23 additions & 0 deletions src/lib/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ const readGithubToken = () => fs.readFile(PATHS.GITHUB_TOKEN_PATH, "utf8")
const writeGithubToken = (token: string) =>
fs.writeFile(PATHS.GITHUB_TOKEN_PATH, token)

const readEnterpriseUrl = async () => {
try {
const txt = await fs.readFile(PATHS.ENTERPRISE_URL_PATH, "utf8")
const trimmed = txt.trim()
return trimmed || undefined
} catch {
return undefined
}
}

const writeEnterpriseUrl = (url?: string) =>
fs.writeFile(PATHS.ENTERPRISE_URL_PATH, url || "")

export const setupCopilotToken = async () => {
const { token, refresh_in } = await getCopilotToken()
state.copilotToken = token
Expand Down Expand Up @@ -44,13 +57,16 @@ export const setupCopilotToken = async () => {

interface SetupGitHubTokenOptions {
force?: boolean
enterpriseUrl?: string
}

export async function setupGitHubToken(
options?: SetupGitHubTokenOptions,
): Promise<void> {
try {
const githubToken = await readGithubToken()
const persistedEnterprise = await readEnterpriseUrl()
if (persistedEnterprise) state.enterpriseUrl = persistedEnterprise

if (githubToken && !options?.force) {
state.githubToken = githubToken
Expand All @@ -63,6 +79,11 @@ export async function setupGitHubToken(
}

consola.info("Not logged in, getting new access token")

// if options passed enterpriseUrl, use it; otherwise use persisted
const enterpriseFromOptions = options?.enterpriseUrl
if (enterpriseFromOptions) state.enterpriseUrl = enterpriseFromOptions

const response = await getDeviceCode()
consola.debug("Device code response:", response)

Expand All @@ -72,6 +93,8 @@ export async function setupGitHubToken(

const token = await pollAccessToken(response)
await writeGithubToken(token)
// persist enterprise url if set
await writeEnterpriseUrl(state.enterpriseUrl)
state.githubToken = token

if (state.showToken) {
Expand Down
3 changes: 3 additions & 0 deletions src/lib/tokenizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ const calculateMessageTokens = (
const tokensPerName = 1
let tokens = tokensPerMessage
for (const [key, value] of Object.entries(message)) {
if (key === "reasoning_opaque") {
continue
}
Comment on lines +76 to +78
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify token counting for reasoning fields.

The code excludes reasoning_opaque from token calculation, but reasoning_text (also added in this PR) is still counted. Confirm this asymmetry is intentional—typically, reasoning/thinking content should be excluded from input token counts since it's model-generated metadata.

Run the following script to check how both reasoning fields are used across the codebase:


🏁 Script executed:

#!/bin/bash
# Search for reasoning_text and reasoning_opaque usage patterns

echo "=== Searching for reasoning_text usage ==="
rg -nC3 'reasoning_text' --type=ts

echo -e "\n=== Searching for reasoning_opaque usage ==="
rg -nC3 'reasoning_opaque' --type=ts

Length of output: 11191


I need to examine the full tokenizer logic to confirm whether reasoning_text is actually being counted and verify if this asymmetry is intentional.

Now let me check the full tokenizer implementation to confirm whether reasoning_text is being counted:

Let me check the complete tokenizer implementation to see if reasoning_text is actually being counted:

Exclude reasoning_text from token counting like reasoning_opaque.

The Anthropic API automatically ignores thinking blocks from previous turns and they are not included when calculating context usage. Both reasoning_text and reasoning_opaque in this codebase represent model-generated metadata from Anthropic responses (the thinking and encrypted signature fields respectively). When these are passed back in messages for multi-turn conversations, they should both be excluded from input token counting. Currently, only reasoning_opaque is skipped at lines 76-78; reasoning_text proceeds through normal token counting, creating an asymmetry that should be resolved.

Add a check to also skip reasoning_text:

if (key === "reasoning_opaque" || key === "reasoning_text") {
  continue
}
🤖 Prompt for AI Agents
In src/lib/tokenizer.ts around lines 76 to 78, the tokenizer currently skips
only "reasoning_opaque" but still counts "reasoning_text", creating an
unintended asymmetry; update the conditional to also skip "reasoning_text" so
both model-generated thinking fields are excluded from input token counting
(i.e., treat "reasoning_text" the same as "reasoning_opaque" and continue past
it).

if (typeof value === "string") {
tokens += encoder.encode(value).length
}
Expand Down
16 changes: 16 additions & 0 deletions src/lib/url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export function normalizeDomain(url: string | undefined): string | undefined {
if (!url) return undefined
return url.replace(/^https?:\/\//, "").replace(/\/+$/, "")
}

export function githubBaseUrl(enterprise?: string): string {
if (!enterprise) return "https://github.com"
const domain = normalizeDomain(enterprise)
return `https://${domain}`
}

export function githubApiBaseUrl(enterprise?: string): string {
if (!enterprise) return "https://api.github.com"
const domain = normalizeDomain(enterprise)
return `https://api.${domain}`
}
2 changes: 2 additions & 0 deletions src/routes/messages/anthropic-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface AnthropicToolUseBlock {
export interface AnthropicThinkingBlock {
type: "thinking"
thinking: string
signature: string
}

export type AnthropicUserContentBlock =
Expand Down Expand Up @@ -196,6 +197,7 @@ export interface AnthropicStreamState {
messageStartSent: boolean
contentBlockIndex: number
contentBlockOpen: boolean
thinkingBlockOpen: boolean
toolCalls: {
[openAIToolIndex: number]: {
id: string
Expand Down
1 change: 1 addition & 0 deletions src/routes/messages/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export async function handleCompletion(c: Context) {
contentBlockIndex: 0,
contentBlockOpen: false,
toolCalls: {},
thinkingBlockOpen: false,
}

for await (const rawEvent of response) {
Expand Down
Loading