mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
chore(release): v3.1.10 — OmniRoute v3.1.9 remaining bug fixes sprint (#720)
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
9ffad1005e
commit
437cf9bab0
21
CHANGELOG.md
21
CHANGELOG.md
@@ -4,6 +4,15 @@
|
||||
|
||||
---
|
||||
|
||||
## [3.1.10] — 2026-03-28
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **Fix #706** — Fixed icon fallback rendering caused by Tailwind V4 `font-sans` override by applying `!important` to `.material-symbols-outlined`.
|
||||
- **Fix #703** — Fixed GitHub Copilot broken streams by enabling `responses` to `openai` format translation for any custom models leveraging `apiFormat: "responses"`.
|
||||
- **Fix #702** — Replaced flat-rate usage tracking with accurate DB pricing calculations for both streaming and non-streaming responses.
|
||||
- **Fix #716** — Cleaned up Claude tool-call translation state, correctly parsing streaming arguments and preventing OpenAI `tool_calls` chunks from repeating the `id` field.
|
||||
|
||||
## [3.1.9] — 2026-03-28
|
||||
|
||||
### ✨ New Features
|
||||
@@ -31,12 +40,12 @@
|
||||
|
||||
### 📁 New Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `open-sse/translator/helpers/schemaCoercion.ts` | Schema coercion and tool description sanitization utilities |
|
||||
| `tests/unit/schema-coercion.test.mjs` | Unit tests for schema coercion |
|
||||
| `tests/unit/t40-opencode-cli-tools-integration.test.mjs` | CLI tool integration tests |
|
||||
| `COVERAGE_PLAN.md` | Test coverage planning document |
|
||||
| File | Purpose |
|
||||
| -------------------------------------------------------- | ----------------------------------------------------------- |
|
||||
| `open-sse/translator/helpers/schemaCoercion.ts` | Schema coercion and tool description sanitization utilities |
|
||||
| `tests/unit/schema-coercion.test.mjs` | Unit tests for schema coercion |
|
||||
| `tests/unit/t40-opencode-cli-tools-integration.test.mjs` | CLI tool integration tests |
|
||||
| `COVERAGE_PLAN.md` | Test coverage planning document |
|
||||
|
||||
## [3.1.8] - 2026-03-27
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: OmniRoute API
|
||||
version: 3.1.9
|
||||
version: 3.1.10
|
||||
description: |
|
||||
OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible
|
||||
endpoint that routes requests to multiple AI providers with load balancing,
|
||||
|
||||
@@ -27,6 +27,8 @@ import {
|
||||
saveCallLog,
|
||||
} from "@/lib/usageDb";
|
||||
import { getLoggedInputTokens, getLoggedOutputTokens } from "@/lib/usage/tokenAccounting";
|
||||
import { recordCost } from "@/domain/costRules";
|
||||
import { calculateCost } from "@/lib/usage/costCalculator";
|
||||
import { CLAUDE_OAUTH_TOOL_PREFIX } from "../translator/request/openai-to-claude.ts";
|
||||
import {
|
||||
getModelNormalizeToolCallId,
|
||||
@@ -1315,6 +1317,11 @@ export async function handleChatCore({
|
||||
});
|
||||
}
|
||||
|
||||
if (apiKeyInfo?.id && usage) {
|
||||
const estimatedCost = await calculateCost(provider, model, usage);
|
||||
if (estimatedCost > 0) recordCost(apiKeyInfo.id, estimatedCost);
|
||||
}
|
||||
|
||||
// Translate response to client's expected format (usually OpenAI)
|
||||
// Pass toolNameMap so Claude OAuth proxy_ prefix is stripped in tool_use blocks (#605)
|
||||
let translatedResponse = needsTranslation(targetFormat, sourceFormat)
|
||||
@@ -1447,22 +1454,29 @@ export async function handleChatCore({
|
||||
apiKeyName: apiKeyInfo?.name || null,
|
||||
noLog: apiKeyInfo?.noLog === true,
|
||||
}).catch(() => {});
|
||||
|
||||
if (apiKeyInfo?.id && streamUsage) {
|
||||
calculateCost(provider, model, streamUsage)
|
||||
.then((estimatedCost) => {
|
||||
if (estimatedCost > 0) recordCost(apiKeyInfo.id, estimatedCost);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
};
|
||||
|
||||
// For Codex provider, translate response from openai-responses to openai (Chat Completions) format
|
||||
// For providers using Responses API format, translate stream back to openai (Chat Completions) format
|
||||
// UNLESS client is Droid CLI which expects openai-responses format back
|
||||
const isDroidCLI =
|
||||
userAgent?.toLowerCase().includes("droid") || userAgent?.toLowerCase().includes("codex-cli");
|
||||
const needsCodexTranslation =
|
||||
provider === "codex" &&
|
||||
const needsResponsesTranslation =
|
||||
targetFormat === FORMATS.OPENAI_RESPONSES &&
|
||||
sourceFormat === FORMATS.OPENAI &&
|
||||
!isResponsesEndpoint &&
|
||||
!isDroidCLI;
|
||||
|
||||
if (needsCodexTranslation) {
|
||||
// Codex returns openai-responses, translate to openai (Chat Completions) that clients expect
|
||||
log?.debug?.("STREAM", `Codex translation mode: openai-responses → openai`);
|
||||
if (needsResponsesTranslation) {
|
||||
// Provider returns openai-responses, translate to openai (Chat Completions) that clients expect
|
||||
log?.debug?.("STREAM", `Responses translation mode: openai-responses → openai`);
|
||||
transformStream = createSSETransformStreamWithLogger(
|
||||
"openai-responses",
|
||||
"openai",
|
||||
|
||||
@@ -90,7 +90,6 @@ export function claudeToOpenAIResponse(chunk, state) {
|
||||
tool_calls: [
|
||||
{
|
||||
index: toolCall.index,
|
||||
id: toolCall.id,
|
||||
function: { arguments: delta.partial_json },
|
||||
},
|
||||
],
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "3.1.9",
|
||||
"version": "3.1.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "omniroute",
|
||||
"version": "3.1.9",
|
||||
"version": "3.1.10",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "3.1.9",
|
||||
"version": "3.1.10",
|
||||
"description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
||||
@@ -219,7 +219,7 @@ body {
|
||||
|
||||
/* Material Symbols */
|
||||
.material-symbols-outlined {
|
||||
font-family: "Material Symbols Outlined", sans-serif;
|
||||
font-family: "Material Symbols Outlined", sans-serif !important;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
|
||||
@@ -42,7 +42,6 @@ import {
|
||||
import { markAccountExhaustedFrom429 } from "../../domain/quotaCache";
|
||||
import { RequestTelemetry, recordTelemetry } from "../../shared/utils/requestTelemetry";
|
||||
import { generateRequestId } from "../../shared/utils/requestId";
|
||||
import { recordCost } from "../../domain/costRules";
|
||||
import { logAuditEvent } from "../../lib/compliance/index";
|
||||
import { enforceApiKeyPolicy } from "../../shared/utils/apiKeyPolicy";
|
||||
import {
|
||||
@@ -421,7 +420,6 @@ async function handleSingleModelChat(
|
||||
|
||||
if (result.success) {
|
||||
clearModelUnavailability(provider, model);
|
||||
recordCostIfNeeded(apiKeyInfo, result);
|
||||
if (telemetry) telemetry.startPhase("finalize");
|
||||
if (telemetry) telemetry.endPhase();
|
||||
return result.response;
|
||||
@@ -670,18 +668,6 @@ async function executeChatWithBreaker({
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record cost if API key has budget tracking enabled.
|
||||
*/
|
||||
function recordCostIfNeeded(apiKeyInfo: any, result: any) {
|
||||
if (!apiKeyInfo?.id) return;
|
||||
try {
|
||||
const usage = result.usage || {};
|
||||
const estimatedCost = ((usage.prompt_tokens || 0) + (usage.completion_tokens || 0)) * 0.000001;
|
||||
if (estimatedCost > 0) recordCost(apiKeyInfo.id, estimatedCost);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// ──── Extracted helpers (T-28) ────
|
||||
|
||||
function handleNoCredentials(
|
||||
|
||||
@@ -79,6 +79,8 @@ test("updateModelComboMapping merges fields and returns the refreshed mapping",
|
||||
priority: 1,
|
||||
});
|
||||
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
|
||||
const updated = await mappingsDb.updateModelComboMapping(created.id, {
|
||||
pattern: "claude-*",
|
||||
comboId: comboB.id,
|
||||
|
||||
Reference in New Issue
Block a user