Commit Graph

14 Commits

Author SHA1 Message Date
2305e26e25 feat: pre-request token validation, mid-stream error handling, and usage stats improvements
- Qoder: handle mid-stream errors by returning proper error Response instead of embedding in stream
- Qoder: add refreshCredentials() to validate token via quota endpoint before requests
- chatCore: validate and refresh provider tokens before sending chat requests
- chatCore: bail early with 401 on unrecoverable token refresh errors
- Usage stats: track apiKey, comboName, fallbackHistory in request details
- Dashboard: improve Combos, Endpoint, Provider, Usage, and RequestDetails pages
- API keys route: upsert logic with provider_type support
- DB repos: usageRepo query improvements, requestDetailsRepo pagination, apiKeysRepo updates
2026-06-29 10:08:07 +07:00
1fe8115dad merge: resolve conflicts with origin/master - keep both local and remote features 2026-06-22 11:40:16 +07:00
98412aa0bb update: sync local changes with latest features 2026-06-22 11:26:25 +07:00
1cf55126f5 fix(qoder): remove orphaned code causing build failure
Removed leftover code from incomplete edit that caused syntax error.
2026-06-19 09:40:48 +07:00
ba4ee30122 fix(qoder): detect errors in SSE envelope to trigger failover
- Made wrapQoderSSE async to peek at first chunk before streaming
- Parse first SSE line to check for error envelope (statusCodeValue !== 200)
- If error detected, return error Response with proper HTTP status code
- This triggers chatCore's !providerResponse.ok check and failover logic
- Fixes issue where qoder 403 quota errors were wrapped as successful streams

Before: qoder errors appeared as stream content with finish_reason: 'stop'
After: qoder errors return proper HTTP error codes, triggering provider failover
2026-06-19 08:47:25 +07:00
decolua
bb9e9aa91f refactor(open-sse): registry consolidation + DRY media/oauth/adhoc cleanup
- Single-source registry: oauth clientId/tokenUrl, usage URLs, image/embed
  configs, search defaultModel, codex fixedPort, google token url derive.
- Remove 29 unused OmniRoute providers (registry 100→71); media intact.
- De-adhoc: codex literals → registry format/oauth flags; reasoningInject,
  image/embed openrouter headers + xai bodyFields config-driven.
- Add REGISTRY_TEMPLATE.js + expand PROVIDER_DEFAULTS/schema JSDoc.
- Baselines updated; PROVIDERS 62 + alias 90 byte-for-byte, golden snapshots.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 13:15:48 +07:00
decolua
6ee4555821 refactor(open-sse): DRY SSE primitives into utils/sseConstants.js
Gom SSE_DONE + SSE_HEADERS + SSE_HEADERS_NO_BUFFER vào 1 file không
phụ thuộc (tránh kéo usageDb vào executor). Áp cho kiro, cursor, qoder,
github, commandcode, perplexity-web, grok-web. Byte-for-byte verified
(headers/DONE giữ nguyên; biến thể no-buffer dùng const riêng).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-13 21:42:59 +07:00
decolua
137a25e9ac fix(qoder): increase timeouts for reasoning models and improve stream handling 2026-06-08 09:17:33 +07:00
therunnas
61d546627e fix(qoder): allow qmodel_latest model key
- Add qmodel_latest to QODER_MODEL_MAP
- Expose qmodel_latest in static Qoder provider catalog (qd)
- Generalize executor comment so model set does not go stale
- Add unit coverage for the new model key + catalog

Closes #1638

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-06 10:44:37 +07:00
zhangweihong
12c97ad46f feat(qoder): fetch latest model + nút import model trên dashboard
Merge PR #1642 (decolua/9router) — chỉ lấy code + i18n, bỏ test/docs/package.json.

- qoder.js: bỏ guard QODER_MODEL_MAP cứng, resolve model_config qua dynamic API (hỗ trợ qmodel_latest không cần sửa code)
- dashboard page: thêm nút "Fetch Qoder Models" tự import model list vào aliases
- i18n zh-CN: thêm key cho nút fetch

Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-06 10:22:03 +07:00
Simon Shi
935462ce8f test(qoder): add regression coverage for review-fix changes
Adds 18 new tests covering the bugs fixed in the previous commit so they
can't silently regress:

- parseExpiry (7 tests): numeric ms-epoch input, numeric strings handled
  before Date.parse so "1700000000" doesn't get year-interpreted, RFC3339
  strings, expires_in:0 honored as already-expired, 30-day fallback only
  when both inputs are missing/invalid

- normalizeMessages (4 tests): system hoisting, multipart text flatten,
  multiple system joining, empty input

- wrapQoderSSE (6 tests): the fixed cases — trailing partial line drained
  in flush(), no chunks forwarded after [DONE], embedded newlines stripped
  from inner body, error envelope produces error chunk + [DONE], non-ok
  responses returned unchanged

- expose parseExpiry from auth.js, expose normalizeMessages/wrapQoderSSE
  via __test__ from the executor (internals only — not part of the public
  API). Marked with comment so the surface is intentional.

42 tests total (24 original + 18 new). Build still clean.
2026-05-29 17:36:27 +07:00
Simon Shi
620b59ca0b fix(qoder): address review findings
Correctness:
- testUtils: drop checkExpiry so the userinfo URL probe actually runs (revoked
  tokens used to look "active" until local 30-day expiry passed)
- auth.parseExpiry: handle numeric expiresAt, swap parseInt before Date.parse
  so "2026" doesn't get interpreted as year-2026, treat expires_in:0 as
  already-expired instead of fabricating a 30-day default
- providers.mapTokens: synthesize email from userId when fetchUserInfo fails
  so OAuth dedup works (re-logins no longer accumulate "Account N" rows)

SSE wrapper:
- wrapQoderSSE: add !doneEmitted guard on success branch (chunks could leak
  past [DONE] when an error envelope shared a TCP packet with a valid one)
- flush(): finalize TextDecoder + drain trailing buffer so the chunk carrying
  finish_reason is delivered when upstream closes without a final \n
- sanitize literal \n inside inner OpenAI body so SSE framing stays intact

Robustness:
- executor: wrap buildCosyHeaders in try/catch so a missing accessToken
  returns 401 (re-auth) instead of bubbling as 500
- executor: short-circuit on missing accessToken before signing
- executor: plumb proxyOptions/signal through buildQoderRequestBody so
  proxy-only networks can fetch the model_config catalog
- qoderModels: dedupe concurrent first-time misses with an in-flight Promise
  map (parallel chat windows now do 1 upstream fetch instead of N)
- qoderModels: check signal.aborted before addEventListener so a pre-aborted
  parent signal cancels the inner fetch immediately
- auth: AbortController + 15s timeout on pollDeviceToken / fetchUserInfo to
  prevent hung sockets when openapi.qoder.sh stalls mid-response

UX:
- OAuthModal: derive polling deadline from device-code expires_in (qoder
  publishes 300s; the previous fixed 120s caused timeouts when users took
  more than 2 minutes on the consent page)

Cleanup:
- delete src/lib/oauth/services/qoder.js — referenced removed config fields
  (clientId/clientSecret/tokenUrl/authorizeUrl) and was re-exported from
  services/index.js, so any future caller would TypeError on first use
2026-05-29 17:36:27 +07:00
Simon Shi
a6fd84691b feat(qoder): port Kiro-style provider integration with COSY signing
Replaces the Qoder placeholder with a real free-tier provider:

- Device-flow OAuth: PKCE + nonce generated locally, user authorizes at
  qoder.com/device/selectAccounts, poll openapi.qoder.sh until token
- COSY signing (RSA-1024 + AES-128-CBC + MD5) for chat / model-list
- WAF-bypass body encoding (custom-alphabet base64 + thirds rearrange)
- Live model_config catalog from /algo/api/v2/model/list, cached 1h
- 11 models registered (auto/ultimate/performance/efficient/lite +
  6 frontier *model ids)
- Usage fetcher for openapi.qoder.sh/api/v2/quota/usage
- Dashboard live-models resolver, provider test, OAuth modal hookup
- 24 unit tests covering encoder, PKCE, COSY headers, sigPath stripping
2026-05-29 17:36:27 +07:00
decolua
9708541f6d Fix bug 2026-03-31 15:44:19 +07:00