38 Commits
Author SHA1 Message Date
infosave2007 0b071279ac fix(stream): no choice-less chunks unless the client asks for usage
CI / lint (push) Canceled after 0s
CI / test (macos-14) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
Docker / publish (push) Canceled after 0s
Every stream used to carry two chunks with an empty choices array: usage,
which the upstream emits BEFORE the stop reason, and a cortiq_cost_usd chunk
the gateway added itself. OpenAI ships the usage chunk only when the request
sets stream_options: {"include_usage": true}, and chat UIs do not set it -
which is why the same tool, in the same Open WebUI, sees neither chunk from
Groq and two from us.

Honour the flag. With no stream_options a stream is now exactly
role -> content... -> tool_calls -> finish_reason -> [DONE], with no
choice-less chunks at all. When usage is requested it ships once, after the
stop reason, with cortiq_cost_usd folded into the same chunk rather than
trailing behind it.

Token accounting, cost and the X-Cortiq-* headers are untouched: those numbers
were never read back off the wire.

Verified: the default stream matches the reference shape byte for byte, the
opt-in stream matches OpenAI's, the official OpenAI SDK reassembles the
streamed tool call, and the full Open WebUI 0.11.0 tool loop still runs the
tool and answers from its result.
2026-08-21 21:24:11 +03:00
infosave2007 9fd4bdaa85 fix(tools): never deliver a tool call twice, and make an unparsed one executable
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
Qwen-family templates write a function call into the generated text as
<tool_call>{...}</tool_call>. Runtimes disagree on what to do with it: some
parse it into structured tool_calls AND still stream the raw markup as
content - the client then gets the same call twice, once as prose it must not
show and once as the real thing - while others never parse it at all, so a
perfectly good call arrives as text no tool loop will run.

Settle it in the gateway. A streaming-aware filter strips the markup from
content (handling a marker split across SSE chunks, leaving ordinary text
containing "<" alone, and handing an unterminated block back as text), and
when nothing structured came with it the markup is promoted into a real
tool_calls entry with a generated call_... id. Markup that is not a JSON
object with a name is ignored so a stray marker cannot become a bogus call.

Streamed tool_calls are also brought to the OpenAI shape: index on every
entry, id and type exactly once per call so continuation deltas carrying only
an argument fragment stay legal, and function.arguments always a string. A
reply that carries tool calls now always ends with finish_reason "tool_calls",
whatever the upstream called it. The same cleanup runs on the non-streaming
path, so every protocol adapter benefits.

Verified end to end: Open WebUI 0.11.0 -> gateway -> managed CMF/Qwen with
native function calling. The model returns get_weather({"city":"Paris"}), Open
WebUI executes the tool, feeds the result back, and the assistant answers from
it - no markup in content, finish_reason tool_calls, stream closed with [DONE].
2026-08-21 18:49:19 +03:00
infosave2007 9c15109f26 chore(router): address the hosted router by hostname, TLS verification on
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
router.allaigate.com now resolves and serves a valid certificate, so the
hosted routing API no longer has to be reached by bare IP with certificate
verification switched off. Make https://router.allaigate.com the default
[router].url with verify_tls = true.

Existing configs still pointing at 138.226.222.209 (with or without a scheme
or a trailing slash) are moved to the hostname on load and have verification
restored, then saved: it is the same machine, and this is the difference
between a verified connection and a blind one. A self-hosted router URL is
left untouched.

README, docs/ROUTER.md (both languages), config/gateway.example.toml and
bench/accuracy.py follow.

Verified live: the migration rewrites a legacy config on startup, admin
health reports the hostname reachable over verified TLS (389 ms), and real
requests route through it - "math"/medium 0.416 and "translation"/low 0.268
with route_source=router.
2026-08-21 13:21:24 +03:00
infosave2007 f65f131c41 fix(routing): offered tools must not make a local model unroutable
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
Verified against a real Open WebUI container: it attaches its ~26 built-in
tools (knowledge bases, notes, calendar, ...) to every single message, and
resolve() treated that offer as a hard capability requirement. On a pool of
tools-less local models a plain "hello" therefore failed with "No healthy
model available that satisfies required capabilities", and a pinned local
model was reported as "not available - not in the pool (deleted?)".

In the OpenAI contract `tools` is an offer; only tool_choice: "required" (or
a named function) makes the call impossible without them. Keep preferring a
tool-capable model, but fall back to serving the request with the tools
stripped - together with tool_choice/parallel_tool_calls, which a provider
rejects on their own. A pin whose only shortcoming is tool support is now
honoured; an unknown pin and tool_choice: "required" still fail loudly.

The downgrade is never silent: X-Cortiq-Tools-Dropped: true, plus
cortiq.tools_dropped in the echo block.
2026-08-21 09:59:28 +03:00
infosave2007 b339c2b622 fix(openai): strict OpenAI wire compatibility — Open WebUI can chat again
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
/v1/chat/completions answered ordinary client payloads with a bare HTTP 422
(a plain-text body no OpenAI client can parse), so Open WebUI showed neither
an answer nor an error — it just hung. Parse the body leniently instead:
content as string/null/content-part array, tools: null, stream: null, a
string reasoning_effort, max_completion_tokens, messages[].name and
tool_call_id, and a missing Content-Type. Unreadable bodies now come back as
an OpenAI error envelope with 400.

Responses are strict OpenAI JSON: no "tool_calls": null, finish_reason mapped
to the spec's closed set (cortiq serve's max_tokens -> length), never an empty
id, and `model` echoes the id the client requested — the model that actually
answered stays in cortiq.selected_model and X-Cortiq-Selected-Model.

Streaming is re-normalised rather than forwarded verbatim: a leading role
delta, message-shaped chunks converted to delta, one terminal finish_reason,
always a trailing data: [DONE], and an SSE error event when the upstream dies
mid-flight. Adds X-Accel-Buffering: no so a fronting proxy cannot buffer the
stream into a hang.

GET /v1/models now lists managed [[cmf.servers]] models with their live
running/healthy state, so clients can pin a local .cmf directly.

Also strips client bookkeeping keys (chat_id, session_id, metadata, features,
...) from the upstream call and drops the leftover [to_wire] debug prints.
2026-08-21 09:10:31 +03:00
infosave2007 34093330f6 chore: bump v0.2.44
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
2026-08-08 17:58:25 +03:00
infosave2007 1c59906b49 chore: bump v0.2.43
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
2026-08-08 16:12:17 +03:00
infosave2007 c1ff675d80 chore: release 0.2.42
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
2026-08-08 12:18:04 +03:00
infosave2007 6f4d83ce13 chore: release 0.2.41
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
2026-08-08 09:55:20 +03:00
infosave2007 115b04f20e chore: release 0.2.40
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
2026-08-08 09:13:12 +03:00
infosave2007 b4d36334bf chore: release 0.2.39
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
2026-08-07 22:25:52 +03:00
infosave2007 914ff42fc5 release: v0.2.38 — media generation, task-based routing, ops batch
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
2026-08-07 18:34:00 +03:00
infosave2007 9ec8861464 release: v0.2.37 — zero-argument startup, first-run wizard, real download progress
Startup no longer needs arguments: the config is created on first launch, a busy
port is swapped for a free one, the console is open by default, and the browser
lands on a setup wizard that downloads a ready-made .cmf model and wires it into
routing (managed server + routing.default) when you press Finish.

Download progress was reported from curl's own meter, which prints a full
0→100% bar for the -L redirect hop before the transfer starts — so every job
showed 100% and then hung. Progress now comes from bytes on disk against the
size from the HuggingFace tree (or a HEAD request).

The UI and the gateway also disagreed about the output file name, so a finished
download never marked its card as downloaded; the gateway now returns the exact
stem and the UI sends it back verbatim for download, progress and delete.

Featured models are read live from the infosave HF account in both the wizard
and Import, and all 432 UI strings now exist in every one of the 7 languages.
2026-08-06 17:56:42 +03:00
infosave2007 ad438a5dcb chore: bump v0.2.36
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
2026-07-25 12:01:52 +03:00
infosave2007 5d8455b4a0 v0.2.35
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
2026-07-23 21:25:45 +03:00
infosave2007 0b28aa3d59 release: v0.2.32 mobile admin console 2026-07-22 11:45:00 +03:00
infosave2007 1414ad1b52 release: v0.2.31 - O(1) attention knobs, reasoning toggle & admin token persistence 2026-07-22 11:12:40 +03:00
infosave2007 94c8551206 feat(release): v0.2.30 — per-model generation, O(1) attention, MTP & reasoning parameters 2026-07-22 10:40:44 +03:00
infosave2007 6ecc709fd2 chore(release): v0.2.29 — fix duplicate managed model registration conflict 2026-07-21 23:39:47 +03:00
infosave2007 1661c19550 chore(release): v0.2.28 — auto-detect ready CMF quant & update section title 2026-07-21 23:35:14 +03:00
infosave2007 66ee32b233 chore(release): v0.2.27 — auto-spawn local CMF server on model registration 2026-07-21 23:15:33 +03:00
infosave2007 f6ee045989 chore(release): v0.2.26 — robust multi-GB streaming downloads & ready CMF quants 2026-07-21 23:08:02 +03:00
infosave2007 59e2eb3215 chore(release): v0.2.25 — direct stream download for ready CMF repos 2026-07-21 22:51:58 +03:00
infosave2007 5b743a4691 chore(release): v0.2.24 — new quantizations (q4t, q1t, q1s, q1p, q1) and featured CMF models 2026-07-21 22:45:43 +03:00
infosave2007 630149d141 docs: add docker-compose quickstart (gateway + Ollama) and GHCR badge
- docker-compose.yml + config/gateway.compose.toml (routes to a local Ollama service)
- README (EN+RU): ghcr badge + Docker Compose section
2026-07-01 13:40:24 +03:00
infosave2007 85fc508f04 chore(release): v0.2.2
Benchmarks (latency + accuracy), router docs + LAUNCH1 promo, Docker image on ghcr.
2026-07-01 11:28:56 +03:00
infosave2007 010171e05c feat: Docker image + ghcr publishing
- multi-stage Dockerfile (non-root, embeds SPA, default config, ~160MB) + .dockerignore
- .github/workflows/docker.yml: push to ghcr.io/infosave2007/cortiq-gateway
  (master -> :edge/:sha amd64; tags -> :X.Y.Z/:latest, linux/amd64+arm64)
- README (EN+RU): docker run instructions; CHANGELOG
- verified locally: image builds, container serves /healthz + /admin
2026-07-01 11:23:09 +03:00
infosave2007 39c56145ed docs: document the allaigate router + launch promo
- docs/ROUTER.md + docs/ROUTER.ru.md: what the semantic router does, hosted vs
  self-hosted, config, API contract, pricing, and the LAUNCH1 $1 Starter promo
- README (EN+RU): "The router (allaigate)" section with the promo callout
2026-07-01 10:35:29 +03:00
infosave2007 17c8186383 docs: add task-type accuracy benchmark (router vs keyword heuristic)
- bench/tasks.jsonl (37 natural-language prompts, 7 task types) + bench/accuracy.py
- measured on the live allaigate router: 100% (37/37) vs keyword heuristic 32%
- BENCHMARKS.md accuracy section; README (EN+RU) perf+accuracy tables and test matrix
- run.sh runs accuracy when CORTIQ_ROUTER_KEY is set
2026-07-01 07:45:16 +03:00
infosave2007 6466006de6 docs: add latency benchmark vs LiteLLM/Portkey
- bench/ reproducible harness: instant mock backend + Apache Bench (ab -k -r)
- measures gateway overhead in passthrough mode (apples-to-apples)
- BENCHMARKS.md: methodology, caveats, results (cortiq ~57k rps p99 1ms vs
  Portkey ~5.8k p99 9ms vs LiteLLM 4-workers ~1.2k p99 59ms)
- README (EN+RU): Performance section + highlight
2026-06-30 18:11:26 +03:00
infosave2007 6a25d8ac08 feat: finish remaining inbound protocols (models, completions, native /route)
- GET /v1/models: pool + virtual cortiq-auto
- POST /v1/completions (legacy): non-stream + streaming text_completion
- POST /route: native passthrough returning the routing decision
- all inbound protocol adapters now implemented; admin meta + README/CHANGELOG updated
- bump to 0.2.1
2026-06-30 17:42:45 +03:00
infosave2007 e486617e51 chore(release): v0.2.0
Streaming (SSE), Anthropic provider/inbound, embeddings, semantic cache, MCP server.
2026-06-30 17:17:04 +03:00
infosave2007 729a8bfbba feat: MCP (Model Context Protocol) server
- POST /mcp (JSON-RPC 2.0): initialize, ping, tools/list, tools/call; notifications -> 202
- tools: cortiq_route (classify -> routing decision) and cortiq_chat (route -> answer)
- gated by protocols.mcp; admin meta marks it implemented
- docs: README/README.ru protocol tables, highlights and status updated for
  streaming, Anthropic, embeddings, semantic cache and MCP
2026-06-30 17:12:55 +03:00
infosave2007 5759de153b feat: semantic (embedding-based) response cache
- cache.rs: cosine-similarity store (threshold, TTL, ring buffer), partitioned by
  routing signature; tracks hit rate and model cost saved
- pipeline: embed routing text, look up before the model call (route_source=cache on
  hit), insert successful responses; stats record cache hits
- config [cache] + admin settings card; dashboard shows hit-rate and savings
- embedding model resolved from the pool (or first kind="embedding")
2026-06-30 17:04:53 +03:00
infosave2007 bf8534f13d feat: Anthropic provider/inbound + embeddings
- Anthropic outbound provider: /v1/messages, system extraction, x-api-key +
  anthropic-version; streaming translates Anthropic SSE -> unified OpenAI wire format
- Anthropic inbound adapter (POST /v1/messages): non-streaming JSON + streaming SSE
  (OpenAI -> Anthropic event translation)
- Embeddings: POST /v1/embeddings inbound + Provider::embed(); embedding models
  resolvable from the pool (registry no longer skips them)
- protocols mounted always, gated on live flags; admin meta marks them implemented
2026-06-30 16:50:22 +03:00
infosave2007 70d8cde258 feat: streaming (SSE) end-to-end
- Provider::chat_stream; OpenAI provider streams via stream_options.include_usage
- pipeline.run_stream: shared route/select, failover before first byte
- /v1/chat/completions forwards SSE verbatim with X-Cortiq-* headers; usage tapped for stats
- admin /test/stream + live streaming in Playground (stream toggle, 7 langs)
2026-06-30 16:37:35 +03:00
infosave2007 fd2f2a165b chore(release): prepare v0.1.0 for crates.io
- bump version to 0.1.0; add crates.io metadata (include list, homepage, rust-version)
- add .github/workflows/release.yml: on tag v* → publish to crates.io + GitHub Release with binaries
- README: absolute links/images and badges so it renders on crates.io; add install section
- CHANGELOG: 0.1.0
2026-06-30 13:47:42 +03:00
infosave2007 cbc385f6c7 feat: universal LLM gateway with embedded multilingual admin console
- OpenAI-compatible gateway with intelligent routing via cortiq-router (allaigate)
- Embedded build-free admin SPA at /admin: 7 languages (en/ru/de/fr/es/zh/tr),
  light/dark themes, hot config reload (no restart)
- Admin API, per-request statistics + Prometheus /metrics, secret store, playground
- English documentation (primary) with Russian translations (*.ru.md)
2026-06-30 10:51:37 +03:00