Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions model_cost/model_cost.json
Original file line number Diff line number Diff line change
Expand Up @@ -326,5 +326,9 @@
"gpt-5.4": {
"input_token_price_per_million": 2.5,
"output_token_price_per_million": 15.0
},
"z-ai/glm-4.7": {
"input_token_price_per_million": 0.4,
"output_token_price_per_million": 1.5
}
}
19 changes: 19 additions & 0 deletions router_inference/config/lynkr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"pipeline_params": {
"router_name": "lynkr",
"router_cls_name": "LynkrRouter",
"models": [
"openai/gpt-oss-120b",
"qwen/qwen3-235b-a22b-2507",
"z-ai/glm-4.7"
],
"default_model": "openai/gpt-oss-120b",
"tier_map": {
"SIMPLE": "openai/gpt-oss-120b",
"MEDIUM": "qwen/qwen3-235b-a22b-2507",
"COMPLEX": "z-ai/glm-4.7",
"REASONING": "z-ai/glm-4.7"
},
"description": "Lynkr (github.com/Fast-Editor/Lynkr) — self-hosted LLM gateway; complexity-tier routing via WS7 anchor intent scoring (local embeddings), queried live through Lynkr's /routing/analyze endpoint."
}
}
3,782 changes: 3,782 additions & 0 deletions router_inference/predictions/lynkr-robustness.json

Large diffs are not rendered by default.

190,344 changes: 190,344 additions & 0 deletions router_inference/predictions/lynkr.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions router_inference/router/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from router_inference.router.auto_router import auto_router
from router_inference.router.chuzom_solo_v32 import ChuzomSoloV32Router
from router_inference.router.llm_router import LLMRouter
from router_inference.router.lynkr_router import LynkrRouter

__all__ = [
"BaseRouter",
Expand All @@ -17,4 +18,5 @@
"auto_router",
"LLMRouter",
"ChuzomSoloV32Router",
"LynkrRouter",
]
69 changes: 69 additions & 0 deletions router_inference/router/lynkr_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# SPDX-FileCopyrightText: Copyright contributors to the RouterArena project
# SPDX-License-Identifier: Apache-2.0

"""
Lynkr router adapter.

Queries a running Lynkr gateway (github.com/Fast-Editor/Lynkr) for its
routing decision on each prompt and maps Lynkr's complexity tier
(SIMPLE / MEDIUM / COMPLEX / REASONING) to a model from the config's
tier_map. Lynkr's /routing/analyze?mode=intent endpoint runs the same
WS7 anchor intent scorer used by its live proxy routing, without
performing any LLM inference.

Requires a Lynkr instance (default http://localhost:8081, override with
LYNKR_URL). Lynkr's embedding backend (Ollama nomic-embed-text) should be
up so scoring runs in anchor mode; if embeddings are unavailable Lynkr
falls back to lexical scoring, which is also its live-serving behavior.
"""

import json
import os
import time
import urllib.request

from router_inference.router.base_router import BaseRouter


class LynkrRouter(BaseRouter):
"""Adapter that proxies routing decisions to a live Lynkr gateway."""

def __init__(self, router_name: str):
super().__init__(router_name)
params = self.config["pipeline_params"]
self.tier_map = params["tier_map"]
self.default_model = params.get("default_model", self.models[0])
self.base_url = os.environ.get("LYNKR_URL", "http://localhost:8081")
self.endpoint = f"{self.base_url}/routing/analyze?mode=intent"
for tier, model in self.tier_map.items():
if model not in self.models:
raise ValueError(
f"tier_map[{tier}] = '{model}' is not in the config model pool"
)

def _get_prediction(self, query: str) -> str:
payload = json.dumps({"messages": [{"role": "user", "content": query}]}).encode(
"utf-8"
)
last_err = None
for attempt in range(3):
try:
req = urllib.request.Request(
self.endpoint,
data=payload,
headers={"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(req, timeout=30) as resp:
body = json.loads(resp.read().decode("utf-8"))
tier = body.get("tier")
if tier in self.tier_map:
return self.tier_map[tier]
raise ValueError(f"Lynkr returned unknown tier: {tier!r}")
except (urllib.error.URLError, TimeoutError, ValueError) as err:
last_err = err
time.sleep(1 + attempt)
raise RuntimeError(
f"Lynkr routing failed after 3 attempts ({last_err}). "
f"Is Lynkr running at {self.base_url}?"
)
3 changes: 3 additions & 0 deletions universal_model_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@
"gemini-2.5-flash-lite",
"qwen3-235b-a22b-instruct-2507",
"qwen3-30b-a3b-instruct-2507",
# Lynkr pool additions (all served via OpenRouter)
"openai/gpt-oss-120b",
"z-ai/glm-4.7",
]


Expand Down
Loading