Conversation
el-pablos <yeteprem.end23juni@gmail.com>
- Add sessionId and userId optional fields to RequestContext - Add generateTraceId() for creating unique trace IDs - Add runWithContext() helper for running code with context - Add getTraceId() function (alias getCurrentTraceId as deprecated) el-pablos <yeteprem.end23juni@gmail.com>
…Delta dan ResponseMessage el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
- Test untuk PATHS constants (APP_DIR, GITHUB_TOKEN_PATH) - Test untuk ensurePaths function (directory creation, file permissions) - Test untuk XDG Base Directory spec compliance - 13 test cases, semua passed el-pablos <yeteprem.end23juni@gmail.com>
- Test createHandlerLogger returns valid logger - Test LogEmitter.log adds entry - Test LogEmitter maintains buffer limit (1000 max) - Test LogEmitter emits events to listeners - Test logger has all methods (info, warn, error, debug, success, box) - Test listener subscribe/unsubscribe functionality - Test error handling for listener errors - Split into 3 files untuk compliance dengan max-lines-per-function el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
el-pablos <yeteprem.end23juni@gmail.com>
- Buat integration tests yang comprehensive untuk stream translation - Test mencakup: 1. Stream state initialization 2. Base chunk creation dengan berbagai overrides 3. Event type helpers (findEvent & filterEvents) 4. THINKING_TEXT constant validation 5. Error translation event 6. Chunk structure validation (thinking, reasoning_opaque, tool calls) 7. State machine transitions 8. Event structure validation (semua event types) 9. Finish reason mapping NOTE: Tests ini dirancang untuk kompatibel dengan implementasi stream-translation.ts yang ada sekarang. Tests yang memerlukan handleReasoningOpaqueSignature dan handleToolCallsDelta tidak disertakan karena fungsi-fungsi tersebut belum diimplementasikan. Semua 21 tests pass dengan 74 expect() calls. el-pablos <yeteprem.end23juni@gmail.com>
… override 2M anti compact, max output 128K, fix port config el-pablos <yeteprem.end23juni@gmail.com>
…-robin pool rotation sekarang work dengan benar via messages api el-pablos <yeteprem.end23juni@gmail.com>
…ang belum ada suffix level el-pablos <yeteprem.end23juni@gmail.com>
There was a problem hiding this comment.
Pull request overview
This PR expands the server’s Copilot proxy to better support “thinking/reasoning” outputs and the native /v1/messages flow, alongside configuration and operational updates.
Changes:
- Add a new Copilot “Messages API” client and route
/v1/messagesrequests through either Messages API or Chat Completions based on model endpoint support/capabilities. - Enhance streaming translation to emit Anthropic thinking blocks from
reasoning_text/reasoning_opaque. - Introduce new config knobs (default max output tokens, context override / truncation disable) and add file-based handler logging + account-pool round-robin adjustments.
Reviewed changes
Copilot reviewed 27 out of 31 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Excludes test files from TS compilation/typecheck scope. |
| src/start.ts | Uses config-derived port when it differs from CLI default. |
| src/services/copilot/create-messages.ts | New Copilot client for /v1/messages with beta header handling and streaming support. |
| src/services/copilot/chat-completion-types.ts | Adds reasoning_text / reasoning_opaque fields to chunk/response delta types. |
| src/routes/messages/stream-translation.ts | Translates reasoning fields into Anthropic “thinking” SSE content blocks. |
| src/routes/messages/request-payload.ts | Default max_tokens now comes from config (defaultMaxOutputTokens). |
| src/routes/messages/handler.ts | Major refactor: route to Messages API vs Chat Completions, quota optimizations, thinking block filtering, stream handling. |
| src/routes/messages/tests/thinking-mechanism.test.ts | New unit tests for thinking translation behavior. |
| src/routes/chat-completions/truncate-messages.ts | Adds truncation disable + context-window override; adjusts safety multiplier for Claude. |
| src/lib/request-context.ts | Adds helpers (generateTraceId, runWithContext, getTraceId) and deprecates old accessor. |
| src/lib/paths.ts | Expands PATHS constants; adds ensureDir. |
| src/lib/logger.ts | Adds file-based handler logging + buffering/retention + keeps LogEmitter wrapper. |
| src/lib/config.ts | Adds new config fields + getters (default output tokens, context override, disable truncation). |
| src/lib/account-pool.ts / src/lib/account-pool-selection.ts | Round-robin index/rotation behavior changes + logging on selection. |
| src/lib/tests/* | New tests for paths/logger behaviors. |
| src/tests/integration/stream-translation.integration.test.ts | New integration test scaffold for stream translation (currently contains outdated notes). |
| package.json / README.md | Large metadata/docs updates beyond “thinking” feature scope. |
| eslint.config.js | Ignores all tests from linting. |
| .github/workflows/* | CI trigger changes + new version-bump workflow. |
| .DS_Store / screenshots/.DS_Store | OS-generated files added (shouldn’t be committed). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Set up periodic buffer flushing | ||
| setInterval(flushAllBuffers, FLUSH_INTERVAL_MS) | ||
|
|
There was a problem hiding this comment.
The module sets up a global setInterval(flushAllBuffers, ...) at import time. In Node/Bun this can keep the event loop alive and make bun test hang or become flaky. Consider creating the interval only when file logging is enabled/used, and call unref() on the timer (or provide an explicit start/stop lifecycle hook) so importing the logger doesn’t prevent process exit.
| // Apply quota optimization | ||
| const quotaContext = applyQuotaOptimization(anthropicPayload, c) | ||
|
|
||
| const accountInfo = getAccountInfo() | ||
| applyFallbackIfNeeded(anthropicPayload) | ||
| logRequestStart(anthropicPayload, accountInfo) | ||
|
|
||
| const translatedPayload = translateToOpenAI(anthropicPayload) | ||
|
|
||
| // Apply truncation to fit within model's prompt token limit | ||
| const openAIPayload = await truncateMessages(translatedPayload) | ||
|
|
||
| consola.debug( | ||
| "Translated OpenAI request payload:", | ||
| JSON.stringify(openAIPayload), | ||
| ) | ||
|
|
||
| tokenState.input = estimateInputTokens(openAIPayload.messages) | ||
| // Apply fallback if needed | ||
| applyFallbackIfNeeded(anthropicPayload) | ||
|
|
There was a problem hiding this comment.
The handler still writes to requestCache in the non-streaming response path, but this flow no longer checks requestCache.get(...) before calling upstream (the earlier cache-hit handling was removed). That makes caching effectively write-only and eliminates cache hits. Consider reintroducing a cache lookup for non-streaming requests (before executing the upstream call) or removing the cache-write if caching is intentionally disabled for this route.
| "**/__tests__/**", | ||
| "**/*.test.ts", |
There was a problem hiding this comment.
ESLint is configured to ignore all __tests__ and *.test.ts files, so the newly added tests won’t be linted in CI. If the intent is to rely on linting for code quality, consider removing these ignores (or limiting them to generated fixtures) so tests follow the same standards as production code.
| "**/__tests__/**", | |
| "**/*.test.ts", |
| * NOTE: Some tests are marked as skipped (.skip) because they depend on | ||
| * functions that are not yet implemented in stream-translation.ts: | ||
| * - handleReasoningOpaqueSignature | ||
| * - handleToolCallsDelta | ||
| * | ||
| * These tests will be enabled once those functions are implemented. |
There was a problem hiding this comment.
The header comment says translateChunkToAnthropicEvents references missing functions (handleReasoningOpaqueSignature, handleToolCallsDelta) and that tests are skipped until they’re implemented. In the current code, handleReasoningOpaqueSignature exists and tool calls are handled via handleToolCallsLoop, so this note is now misleading. Please update/remove the comment (and consider adding a real integration test that calls translateChunkToAnthropicEvents end-to-end).
| * NOTE: Some tests are marked as skipped (.skip) because they depend on | |
| * functions that are not yet implemented in stream-translation.ts: | |
| * - handleReasoningOpaqueSignature | |
| * - handleToolCallsDelta | |
| * | |
| * These tests will be enabled once those functions are implemented. | |
| * These tests aim to reflect the current implementation of the stream | |
| * translation logic, including reasoning-opaque handling and tool calls. |
| on: | ||
| push: | ||
| branches: ['*'] | ||
| branches: [main, develop] | ||
| pull_request: | ||
| branches: ['*'] | ||
| branches: [main, develop] |
There was a problem hiding this comment.
CI is now configured to run on pushes only to main and develop. This removes CI coverage for pushes to other branches (feature branches, release branches, etc.), which can allow breakages to go unnoticed until a PR is opened/retargeted. If that’s not intentional, consider restoring push triggers for all branches (or at least ** with excludes).
| /** | ||
| * Unit tests for LogEmitter | ||
| */ | ||
|
|
||
| import { describe, expect, it, mock } from "bun:test"; | ||
|
|
||
| // Mock the state module | ||
| void mock.module("~/lib/state", () => ({ | ||
| state: { | ||
| verbose: false, | ||
| }, | ||
| })); | ||
|
|
There was a problem hiding this comment.
This test file uses semicolons and a formatting style that differs from the rest of the repo’s tests. Since tests are currently excluded from ESLint/typecheck, these inconsistencies (and potential TS issues) won’t be caught in CI. Consider reformatting to match the repo style and re-enabling linting/typechecking for test files.
| const LOG_RETENTION_DAYS = 7 | ||
| const LOG_RETENTION_MS = LOG_RETENTION_DAYS * 24 * 60 * 60 * 1000 | ||
| const CLEANUP_INTERVAL_MS = 24 * 60 * 60 * 1000 | ||
| const LOG_DIR = path.join(PATHS.APP_DIR, "logs") |
There was a problem hiding this comment.
LOG_DIR is derived from PATHS.APP_DIR (~/.local/share/.../logs), but PATHS also defines LOGS_DIR under ~/.config/.../logs and the rest of the code/docs appear to treat ~/.config as the config/log area. Consider using PATHS.LOGS_DIR (and ensuring it exists) to avoid scattering storage locations and duplicating path logic.
| const LOG_DIR = path.join(PATHS.APP_DIR, "logs") | |
| const LOG_DIR = PATHS.LOGS_DIR |
| function generateRequestIdFromPayload( | ||
| payload: AnthropicMessagesPayload, | ||
| sessionId?: string, | ||
| ): string { | ||
| const content = JSON.stringify(payload.messages.slice(-3)) | ||
| const input = (sessionId ?? "") + content + Date.now().toString() | ||
| return Buffer.from(input).toString("base64").slice(0, 32) | ||
| } |
There was a problem hiding this comment.
generateRequestIdFromPayload is described as being for deduplication, but it includes Date.now(), making the output non-deterministic even for identical payloads. If this ID is meant to dedupe/replay-protect requests across retries, generate it deterministically from stable inputs (e.g., sessionId + message content hash) or rename it to reflect that it’s just a unique-per-request ID.
| return activeAccounts[index] | ||
| const selected = activeAccounts[index] | ||
| poolState.currentIndex++ | ||
| consola.info( |
There was a problem hiding this comment.
selectRoundRobinAccount logs the selected account at info level on every request. This can generate a lot of log noise under load and may leak account identifiers into logs unnecessarily. Consider switching this to debug (or gating behind state.verbose) and/or sampling.
| consola.info( | |
| consola.debug( |
| "description": "GitHub Copilot API Proxy dengan fitur enterprise-grade untuk integrasi Claude Code. Mendukung thinking mechanism, multi-account pool, request caching, dan file-based logging.", | ||
| "keywords": [ | ||
| "proxy", | ||
| "github-copilot", | ||
| "openai-compatible" | ||
| "claude-code", | ||
| "api-proxy", | ||
| "anthropic", | ||
| "openai", | ||
| "thinking-mechanism", | ||
| "enterprise", | ||
| "typescript" | ||
| ], | ||
| "homepage": "https://github.com/prassaaa/copilot-api", | ||
| "bugs": "https://github.com/prassaaa/copilot-api/issues", | ||
| "homepage": "https://github.com/el-pablos/copilot-api#readme", | ||
| "bugs": { | ||
| "url": "https://github.com/el-pablos/copilot-api/issues" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/prassaaa/copilot-api.git" | ||
| "url": "https://github.com/el-pablos/copilot-api.git" | ||
| }, | ||
| "author": "Prasetyo Ari Wibowo", | ||
| "license": "MIT", | ||
| "author": "el-pablos <yeteprem.end23juni@gmail.com>", |
There was a problem hiding this comment.
This PR changes package metadata (description/homepage/repository/author) to different owners/URLs, which doesn’t match the PR description about adding thinking/model support. If this isn’t an intentional rebranding/fork handover, it should be reverted or moved to a separate PR to avoid surprising downstream consumers (npm metadata, links, attribution).
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
…k, model types + dokumentasi round-robin bug fix el-pablos <yeteprem.end23juni@gmail.com>
inject xhigh thinking gpt5.x & claude 4.5 model