Skip to content

Commit 43b0c28

Browse files
authored
Label API models consistently, widen provider detection with suffix matching, drop tomllib from (#46)
release scripts
1 parent 6228a9d commit 43b0c28

9 files changed

Lines changed: 94 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ under Unreleased and move into a version section at release time.
77

88
### Added
99

10+
- `register_model()` accepts a `model_format` override for models without a matching extractor; `llm_api()` registers its models as format `"api"`, matching the openai/anthropic integrations instead of `"unknown"`.
11+
- `source_from_base_url()` recognizes more provider hosts (anthropic, mistral, groq, together, deepseek, xai, google, fireworks, cerebras, perplexity, nvidia, huggingface, baseten) instead of falling back to raw hostnames, including suffix matching for per-resource subdomains (Azure OpenAI, Baseten).
1012
- `examples/llm_api_example.py`: raw-HTTP LLM tracking with `wildedge.llm_api()`; existing examples updated to the module-level API (`wildedge.span` / `wildedge.flush` / `wildedge.register_model` instead of threading a client variable)
1113
- Releases ship `llms.txt` and `llms-full.txt` as GitHub release assets: the full documentation for that exact version in one file, generated by `scripts/build_llms_txt.py`. README quickstart rewritten around the module-level API.
1214
- `wildedge doctor --send-test-event`: sends one real span event through the full pipeline and reports the ingest response, proving DSN auth and connectivity end to end. The report gains an `environment` section (`WILDEDGE_*` variables, autoload PYTHONPATH status) plus `config_status` / `connectivity_status` fields.

docs/manual-tracking.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,12 @@ For remote APIs with no local object to inspect, pass a placeholder:
8181

8282
```python
8383
handle = client.register_model(
84-
object(),
84+
None,
8585
model_id="openai/gpt-4o",
86-
source="https://api.openai.com",
86+
source="openai",
8787
family="gpt-4o",
8888
version="2024-08-06",
89+
model_format="api",
8990
)
9091
```
9192

scripts/check_tag_version.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,23 @@
44
from __future__ import annotations
55

66
import os
7+
import re
78
from pathlib import Path
89

9-
import tomllib
10-
1110

1211
def main() -> None:
1312
tag = os.environ["TAG_NAME"]
1413
if not tag.startswith("v"):
1514
raise SystemExit(f"Expected tag to start with 'v', got: {tag}")
1615
tag_version = tag[1:]
1716

18-
data = tomllib.loads(Path("pyproject.toml").read_text())
19-
project_version = data["project"]["version"]
17+
# Regex instead of tomllib so the script also runs on Python 3.10.
18+
match = re.search(
19+
r'^version = "([^"]+)"', Path("pyproject.toml").read_text(), re.MULTILINE
20+
)
21+
if match is None:
22+
raise SystemExit("could not find project version in pyproject.toml")
23+
project_version = match.group(1)
2024

2125
if tag_version != project_version:
2226
raise SystemExit(

tests/test_client.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,31 @@ def test_app_identity_env_override_used_for_paths(monkeypatch):
201201
p.assert_called_once_with("env-app")
202202
d.assert_called_once_with("env-app")
203203
r.assert_called_once_with("env-app")
204+
205+
206+
def test_register_model_defaults_name_to_model_id_and_accepts_format():
207+
from wildedge.client import WildEdge
208+
209+
client = WildEdge(dsn="https://secret@ingest.wildedge.dev/key")
210+
client.registry.register.return_value = (object(), True)
211+
212+
client.register_model(
213+
None, model_id="org/model", source="openrouter", model_format="api"
214+
)
215+
216+
info = client.registry.register.call_args[0][1]
217+
assert info.model_name == "org/model"
218+
assert info.model_source == "openrouter"
219+
assert info.model_format == "api"
220+
221+
222+
def test_register_model_format_defaults_to_unknown():
223+
from wildedge.client import WildEdge
224+
225+
client = WildEdge(dsn="https://secret@ingest.wildedge.dev/key")
226+
client.registry.register.return_value = (object(), True)
227+
228+
client.register_model(None, model_id="org/model")
229+
230+
info = client.registry.register.call_args[0][1]
231+
assert info.model_format == "unknown"

tests/test_integrations_openai.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,19 @@ def register_model(obj, *, model_id=None, source=None, **kwargs):
183183
("", "openai"),
184184
("https://openrouter.ai/api/v1", "openrouter"),
185185
("https://api.openai.com/v1", "openai"),
186-
("https://api.together.xyz/v1", "api.together.xyz"),
186+
("https://api.together.xyz/v1", "together"),
187+
("https://api.anthropic.com", "anthropic"),
188+
("https://api.mistral.ai/v1", "mistral"),
189+
("https://api.groq.com/openai/v1", "groq"),
190+
("https://generativelanguage.googleapis.com/v1beta/openai", "google"),
191+
("https://api.fireworks.ai/inference/v1", "fireworks"),
192+
("https://myresource.openai.azure.com/openai/v1", "azure-openai"),
193+
(
194+
"https://model-abc123.api.baseten.co/environments/production/sync/v1",
195+
"baseten",
196+
),
197+
("https://inference.baseten.co/v1", "baseten"),
198+
("https://unknown.example.com/v1", "unknown.example.com"),
187199
("https://localhost:11434/v1", "localhost"),
188200
],
189201
)

tests/test_llm_api.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,10 @@ def test_noop_without_dsn_end_to_end(monkeypatch):
221221
call.usage(tokens_in=1, tokens_out=2)
222222

223223
assert wildedge.get_client().noop is True
224+
225+
226+
def test_registers_model_as_api_format(client):
227+
with wildedge.llm_api(model="m", provider="p"):
228+
pass
229+
230+
assert client.registered[0]["model_format"] == "api"

wildedge/client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,13 +502,15 @@ def register_model(
502502
family: str | None = None,
503503
version: str | None = None,
504504
quantization: str | None = None,
505+
model_format: str | None = None,
505506
auto_instrument: bool = True,
506507
) -> ModelHandle:
507508
"""
508509
Register a model and return a handle for tracking events.
509510
510511
Auto-extracts metadata from ONNX Runtime and GGUF/llama.cpp objects.
511-
User-supplied kwargs override extracted values.
512+
User-supplied kwargs override extracted values. ``model_format``
513+
applies when no extractor matches (e.g. ``"api"`` for remote models).
512514
"""
513515
overrides = {
514516
k: v
@@ -518,6 +520,7 @@ def register_model(
518520
"family": family,
519521
"version": version,
520522
"quantization": quantization,
523+
"format": model_format,
521524
}.items()
522525
if v is not None
523526
}

wildedge/integrations/common.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,39 @@ def debug_failure(framework: str, context: str, exc: BaseException) -> None:
2626
SOURCE_BY_HOSTNAME: dict[str, str] = {
2727
"api.openai.com": "openai",
2828
"openrouter.ai": "openrouter",
29+
"api.anthropic.com": "anthropic",
30+
"api.mistral.ai": "mistral",
31+
"api.groq.com": "groq",
32+
"api.together.xyz": "together",
33+
"api.deepseek.com": "deepseek",
34+
"api.x.ai": "xai",
35+
"generativelanguage.googleapis.com": "google",
36+
"api.fireworks.ai": "fireworks",
37+
"api.cerebras.ai": "cerebras",
38+
"api.perplexity.ai": "perplexity",
39+
"integrate.api.nvidia.com": "nvidia",
40+
"router.huggingface.co": "huggingface",
41+
"inference.baseten.co": "baseten",
2942
}
3043

44+
# Providers whose endpoints live on per-resource subdomains.
45+
SOURCE_BY_HOSTNAME_SUFFIX: list[tuple[str, str]] = [
46+
(".openai.azure.com", "azure-openai"),
47+
(".api.baseten.co", "baseten"),
48+
]
49+
3150

3251
def source_from_base_url(base_url: str | None) -> str:
3352
hostname = urlparse(base_url.lower()).hostname if base_url else ""
34-
return SOURCE_BY_HOSTNAME.get(hostname or "", hostname or "openai")
53+
if not hostname:
54+
return "openai"
55+
exact = SOURCE_BY_HOSTNAME.get(hostname)
56+
if exact is not None:
57+
return exact
58+
for suffix, source in SOURCE_BY_HOSTNAME_SUFFIX:
59+
if hostname.endswith(suffix):
60+
return source
61+
return hostname
3562

3663

3764
def _msg_role(m) -> str | None:

wildedge/llm_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def _handle(self) -> ModelHandle | None:
179179

180180
try:
181181
return get_client().register_model(
182-
None, model_id=self.model, source=self.source
182+
None, model_id=self.model, source=self.source, model_format="api"
183183
)
184184
except Exception as exc:
185185
logger.debug("wildedge: llm model registration failed: %s", exc)

0 commit comments

Comments
 (0)