Skip to content

Dev tama#7

Open
el-pablos wants to merge 30 commits intoprassaaa:mainfrom
el-pablos:dev-tama
Open

Dev tama#7
el-pablos wants to merge 30 commits intoprassaaa:mainfrom
el-pablos:dev-tama

Conversation

@el-pablos
Copy link
Copy Markdown
Contributor

- 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>
- 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>
- 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>
…k, model types + dokumentasi round-robin bug fix

el-pablos <yeteprem.end23juni@gmail.com>
Copilot AI review requested due to automatic review settings March 24, 2026 00:48
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the proxy to better support “thinking/reasoning” behavior (including Claude-style thinking blocks), adds a native /v1/messages (Anthropic) upstream integration path, and introduces new operational features like file-based logging and configurable truncation/output limits.

Changes:

  • Add Messages API client/service and route-level logic to choose between Copilot Chat Completions vs native /v1/messages, including adaptive/enabled thinking injection and new reasoning fields (reasoning_text, reasoning_opaque) in streaming translation.
  • Introduce config options for default output tokens and truncation controls (override/disable), plus related truncation behavior updates.
  • Add file-based handler logger + request context helpers, and expand test coverage and documentation.

Reviewed changes

Copilot reviewed 34 out of 40 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
tsconfig.json Excludes tests from TypeScript compilation.
tests/create-messages.test.ts New unit tests for createMessages service behavior and headers.
src/webui/routes.ts Removes model-mappings route; adds force param to version-check API call.
src/start.ts Attempts to allow config/env port override when CLI port is default.
src/services/copilot/get-models.ts Extends model “supports” metadata for thinking budget/adaptive thinking.
src/services/copilot/create-messages.ts New /v1/messages upstream client with header handling + beta filtering.
src/services/copilot/chat-completion-types.ts Adds reasoning fields and thinking_budget to payload/types.
src/routes/messages/stream-translation.ts Adds reasoning/thinking streaming translation helpers and tool-call loop refactor.
src/routes/messages/request-payload.ts Changes default max_tokens resolution to be config-driven.
src/routes/messages/non-stream-translation.ts Adds thinking budget calculation + OpenAI→Anthropic payload “re-translation” helper.
src/routes/messages/handler.ts Major routing refactor for Messages API vs Chat Completions + thinking injection + token accounting for messages streaming.
src/routes/messages/tests/thinking-mechanism.test.ts New unit tests for thinking/opaque signature event translation.
src/routes/chat-completions/truncate-messages.ts Adds config-based truncation disable/override; adjusts safety multiplier for Claude.
src/routes/chat-completions/normalize-payload.ts Auto-injects reasoning_effort from config for GPT-5 models without suffix.
src/lib/version-check.ts Changes remote commit check to ls-remote and adds forced refresh option.
src/lib/request-context.ts Adds trace ID helpers and context runner utilities.
src/lib/paths.ts Expands XDG-style paths and adds ensureDir.
src/lib/logger.ts Adds file-based handler logging with buffering/retention + keeps LogEmitter for compatibility.
src/lib/config.ts Adds default max output tokens + truncation override/disable controls and model effort tweaks.
src/lib/account-pool.ts Adjusts round-robin state persistence and currentIndex behavior on rotation/error.
src/lib/account-pool-selection.ts Fixes round-robin counter logic; adds selection logging.
src/lib/tests/paths.test.ts New tests for PATHS/ensurePaths behavior.
src/lib/tests/logger-wrapper.test.ts New tests for LogEmitter-backed logger wrapper.
src/lib/tests/logger-handler.test.ts New tests for createHandlerLogger.
src/lib/tests/logger-emitter.test.ts New tests for LogEmitter behavior.
src/tests/integration/stream-translation.integration.test.ts New “integration” test scaffold for stream translation/types/helpers.
screenshots/.DS_Store Adds macOS metadata file under screenshots directory.
README.md Large documentation overhaul (usage, architecture, config, endpoints).
package.json Updates package metadata and keywords/description.
eslint.config.js Excludes tests from linting.
docs/caching-analysis.md Adds detailed cache comparison/analysis doc.
docs/01-round-robin-bug.md Adds a round-robin bug report + fix documentation.
ANALISIS_PERBANDINGAN_CINA_COPILOT_VS_COPILOT_API.md Adds a comprehensive comparison report between projects.
.gitignore Adds patterns for env/secrets/tokens/config secrets.
.github/workflows/version-bump.yml Adds a manual version bump workflow.
.github/workflows/ci.yml Splits CI into test + build jobs and narrows branch triggers.
.DS_Store Adds macOS metadata file at repo root.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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")
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

LOG_DIR is derived from PATHS.APP_DIR even though PATHS already exposes LOGS_DIR (under ~/.config/...). This makes log location inconsistent with the central paths configuration and can scatter logs across multiple base dirs. Consider switching to PATHS.LOGS_DIR (or removing LOGS_DIR if logs are intended to live under APP_DIR).

Suggested change
const LOG_DIR = path.join(PATHS.APP_DIR, "logs")
const LOG_DIR = PATHS.LOGS_DIR

Copilot uses AI. Check for mistakes.
Comment on lines +137 to +158
// Set up periodic buffer flushing
setInterval(flushAllBuffers, FLUSH_INTERVAL_MS)

// Cleanup on process exit
const cleanup = () => {
flushAllBuffers()
for (const stream of logStreams.values()) {
stream.end()
}
logStreams.clear()
logBuffers.clear()
}

process.on("exit", cleanup)
process.on("SIGINT", () => {
cleanup()
process.exit(0)
})
process.on("SIGTERM", () => {
cleanup()
process.exit(0)
})
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

This module sets up a global setInterval(...) and registers process.on('exit'|'SIGINT'|'SIGTERM') handlers at import time. In test runners (and sometimes in CLIs), an unref'd interval and global signal handlers can keep the process alive or cause hard-to-debug side effects. Consider moving this initialization behind an explicit init function (called from server startup), and/or unref() the interval and avoid installing signal handlers in library modules.

Copilot uses AI. Check for mistakes.
Comment on lines 205 to +210
const config = await loadConfig()

// Use config.port if different from default (CLI arg default is 4141)
// This allows config file and PORT env var to override the default
const actualPort = config.port !== 4141 ? config.port : options.port

Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

actualPort uses config.port whenever it differs from 4141, even if the user explicitly passed --port. This makes CLI port overrides ineffective when config sets a non-default port. Consider tracking whether the CLI arg was explicitly provided (or using undefined when not provided) and applying a clear precedence (commonly CLI > env > config > default).

Copilot uses AI. Check for mistakes.
Comment on lines 364 to +369
function resolveMaxTokens(rawMaxTokens: unknown): number {
if (typeof rawMaxTokens === "number" && Number.isFinite(rawMaxTokens)) {
return rawMaxTokens
}
return 4096
// Use configured default for better output capability
return getDefaultMaxOutputTokens()
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

Defaulting max_tokens to config.defaultMaxOutputTokens (32k) without clamping to the selected model's max_output_tokens can make requests invalid for many models and cause upstream 400/422 errors when clients omit max_tokens. Consider keeping a conservative default, or clamping the configured default to the resolved model limit during normalization.

Copilot uses AI. Check for mistakes.
Comment on lines +471 to +476
// For truncation, we just need to update the messages count
// while preserving the original Anthropic format
const truncatedMessageCount = openAIPayload.messages.filter(
(m) => m.role !== "system",
).length

Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

translateOpenAIPayloadToAnthropic derives truncatedMessageCount from the OpenAI message array. However, translateAnthropicMessagesToOpenAI() can expand a single Anthropic user message into multiple OpenAI messages (e.g., tool + user), so this count is not equivalent to the original Anthropic messages.length. This can cause the wrong Anthropic messages to be kept/dropped after truncation (and can break tool_use/tool_result pairing).

Copilot uses AI. Check for mistakes.
return activeAccounts[index]
const selected = activeAccounts[index]
poolState.currentIndex++
consola.info(
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

consola.info(...) inside selectRoundRobinAccount() will log on every request, which can flood logs and impact performance in high-traffic setups. Consider downgrading this to debug (or guard it behind state.verbose / a config flag).

Suggested change
consola.info(
consola.debug(

Copilot uses AI. Check for mistakes.
Comment on lines +44 to +72
describe("ensurePaths", () => {
const testDir = path.join(os.tmpdir(), "copilot-api-paths-test")

beforeEach(async () => {
// Clean up test directory if exists
try {
await fs.rm(testDir, { recursive: true, force: true })
} catch {
// Ignore if doesn't exist
}
})

afterEach(async () => {
// Clean up after tests
try {
await fs.rm(testDir, { recursive: true, force: true })
} catch {
// Ignore if doesn't exist
}
})

it("ensurePaths creates APP_DIR directory", async () => {
// Call ensurePaths (it will create the actual APP_DIR)
await ensurePaths()

// Verify directory exists
const stat = await fs.stat(PATHS.APP_DIR)
expect(stat.isDirectory()).toBe(true)
})
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

These tests call ensurePaths() which creates real directories/files under PATHS.APP_DIR in the user's home directory. The testDir variable is cleaned up but never used, so the tests can leave artifacts on developer machines. Consider mocking PATHS/os.homedir() (or refactoring ensurePaths to accept a base dir) so tests operate entirely in a temp directory.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +16
* 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.
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

The top-level doc comment (and the later NOTE) says helper functions in stream-translation.ts are “not yet implemented” and that translateChunkToAnthropicEvents would throw, but those helpers now exist. This comment is now misleading and may cause skipped coverage to remain unnoticed—please update the note and consider enabling the previously skipped integration scenarios.

Suggested change
* 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.
* NOTE: The helper functions used by these tests (including
* handleReasoningOpaqueSignature and handleToolCallsDelta) are now implemented
* in stream-translation.ts. Tests in this file are expected to run without
* being skipped due to missing helper implementations; any remaining skips
* should be reviewed and documented with up-to-date reasons.

Copilot uses AI. Check for mistakes.
Comment on lines +15 to 22
"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"
},
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

Package metadata points to el-pablos/copilot-api for homepage, bugs.url, and repository.url, while other parts of this PR (e.g., README badges/links) point to prassaaa/copilot-api. This inconsistency can confuse users and break npm links. Consider aligning these fields to the actual canonical repository for this package/fork.

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +53
function getRemoteCommit(branch: string): string {
const output = execSync(`git ls-remote --heads origin ${branch}`, {
stdio: "pipe",
})
.toString()
.trim()

const [sha] = output.split(/\s+/)
if (!sha) {
throw new Error(`Cannot resolve origin/${branch}`)
}

return sha
}

function isLocalUpToDate(local: string, remote: string): boolean {
if (local === remote) {
return true
}

try {
execSync(`git merge-base --is-ancestor ${remote} ${local}`, {
stdio: "ignore",
})
return true
} catch {
return false
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

git ls-remote returns a commit SHA that is not guaranteed to exist in the local object database. git merge-base --is-ancestor ${remote} ${local} can fail (even when local is up-to-date or ahead) unless the remote commit has been fetched. Consider fetching the remote branch (as before) or using a comparison that doesn’t require the remote commit object to be present locally.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants