@miller-tech/uap 1.20.38 → 1.20.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -8206,14 +8206,25 @@ def _parse_anthropic_sse_to_message(raw: bytes) -> dict | None:
|
|
|
8206
8206
|
|
|
8207
8207
|
@app.get("/v1/models")
|
|
8208
8208
|
async def models():
|
|
8209
|
-
"""Return available model list
|
|
8209
|
+
"""Return available model list.
|
|
8210
|
+
|
|
8211
|
+
Advertises Shannon's three canonical Claude model IDs (haiku 4.5,
|
|
8212
|
+
sonnet 4.6, opus 4.7) for client compatibility — Anthropic SDKs
|
|
8213
|
+
typically check /v1/models for the requested ID before sending a
|
|
8214
|
+
Messages request, and failing that check produces a confusing 404 even
|
|
8215
|
+
though the proxy itself would happily accept the request.
|
|
8216
|
+
|
|
8217
|
+
Whether requests for those Claude IDs actually round-trip to
|
|
8218
|
+
api.anthropic.com depends on ANTHROPIC_PASSTHROUGH_MODELS /
|
|
8219
|
+
DEFAULT_PASSTHROUGH_MODEL_PATTERNS. When the local-only sentinel
|
|
8220
|
+
ANTHROPIC_PASSTHROUGH_MODELS=__local_only__ is set, all IDs (including
|
|
8221
|
+
the Claude ones below) are served by the local llama.cpp backend.
|
|
8222
|
+
"""
|
|
8210
8223
|
return {
|
|
8211
8224
|
"data": [
|
|
8212
|
-
{"id": "claude-
|
|
8213
|
-
{"id": "claude-sonnet-4-6
|
|
8214
|
-
{"id": "
|
|
8215
|
-
{"id": "gpt-5.3-codex", "object": "model"},
|
|
8216
|
-
{"id": "claude-opus-4-6-20250616", "object": "model"},
|
|
8225
|
+
{"id": "claude-haiku-4-5-20251001", "object": "model"},
|
|
8226
|
+
{"id": "claude-sonnet-4-6", "object": "model"},
|
|
8227
|
+
{"id": "claude-opus-4-7", "object": "model"},
|
|
8217
8228
|
{"id": "qwen35-a3b-iq4xs", "object": "model"},
|
|
8218
8229
|
]
|
|
8219
8230
|
}
|
|
@@ -5003,3 +5003,57 @@ class TestOpenAIPassthroughConversion(unittest.TestCase):
|
|
|
5003
5003
|
self.assertEqual(tc["function"]["name"], "Bash")
|
|
5004
5004
|
# Arguments are JSON-stringified per OpenAI spec
|
|
5005
5005
|
self.assertEqual(json.loads(tc["function"]["arguments"]), {"command": "pwd"})
|
|
5006
|
+
|
|
5007
|
+
|
|
5008
|
+
class TestModelsEndpoint(unittest.TestCase):
|
|
5009
|
+
"""Tests for the /v1/models discovery endpoint.
|
|
5010
|
+
|
|
5011
|
+
Pins the exact set of model IDs the proxy advertises so an accidental
|
|
5012
|
+
list rewrite that drops a Shannon-required ID fails CI loudly. Driven
|
|
5013
|
+
by Shannon-keygraph's .env defaults (ANTHROPIC_SMALL/MEDIUM/LARGE_MODEL)
|
|
5014
|
+
which expect haiku-4-5, sonnet-4-6, opus-4-7 to be discoverable."""
|
|
5015
|
+
|
|
5016
|
+
def test_models_endpoint_returns_shannon_canonical_set(self):
|
|
5017
|
+
"""The advertised list must contain Shannon's canonical Claude IDs
|
|
5018
|
+
plus the local model. Order is not asserted (clients shouldn't
|
|
5019
|
+
depend on it), but membership is."""
|
|
5020
|
+
import asyncio
|
|
5021
|
+
result = asyncio.run(proxy.models())
|
|
5022
|
+
|
|
5023
|
+
self.assertIn("data", result)
|
|
5024
|
+
ids = {entry["id"] for entry in result["data"]}
|
|
5025
|
+
|
|
5026
|
+
# Shannon-required Claude IDs (must include — per project policy)
|
|
5027
|
+
self.assertIn("claude-haiku-4-5-20251001", ids)
|
|
5028
|
+
self.assertIn("claude-sonnet-4-6", ids)
|
|
5029
|
+
self.assertIn("claude-opus-4-7", ids)
|
|
5030
|
+
|
|
5031
|
+
# Local model (kept because requests for it actually route locally
|
|
5032
|
+
# even with __local_only__ passthrough sentinel set)
|
|
5033
|
+
self.assertIn("qwen35-a3b-iq4xs", ids)
|
|
5034
|
+
|
|
5035
|
+
def test_models_endpoint_drops_stale_4_6_dated_variants(self):
|
|
5036
|
+
"""The pre-2026-05 list advertised claude-opus-4-6-20260101,
|
|
5037
|
+
claude-sonnet-4-6-20250514, claude-opus-4-6-20250616 and
|
|
5038
|
+
unrelated gpt-5 entries. None of those should reappear: the dated
|
|
5039
|
+
4-6 variants are superseded by 4-7, and the proxy doesn't route
|
|
5040
|
+
to OpenAI so the gpt-5 entries were noise."""
|
|
5041
|
+
import asyncio
|
|
5042
|
+
result = asyncio.run(proxy.models())
|
|
5043
|
+
ids = {entry["id"] for entry in result["data"]}
|
|
5044
|
+
|
|
5045
|
+
self.assertNotIn("claude-opus-4-6-20260101", ids)
|
|
5046
|
+
self.assertNotIn("claude-sonnet-4-6-20250514", ids)
|
|
5047
|
+
self.assertNotIn("claude-opus-4-6-20250616", ids)
|
|
5048
|
+
self.assertNotIn("gpt-5.4", ids)
|
|
5049
|
+
self.assertNotIn("gpt-5.3-codex", ids)
|
|
5050
|
+
|
|
5051
|
+
def test_models_endpoint_returns_object_model_for_each_entry(self):
|
|
5052
|
+
"""Each entry must follow the {id, object: 'model'} shape the
|
|
5053
|
+
Anthropic and OpenAI SDKs both expect."""
|
|
5054
|
+
import asyncio
|
|
5055
|
+
result = asyncio.run(proxy.models())
|
|
5056
|
+
|
|
5057
|
+
for entry in result["data"]:
|
|
5058
|
+
self.assertIn("id", entry)
|
|
5059
|
+
self.assertEqual(entry["object"], "model")
|