plyllm 1.0.0__tar.gz
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.
- plyllm-1.0.0/.claude/skills/update-models/SKILL.md +52 -0
- plyllm-1.0.0/.claude/skills/update-models/references/sources.md +198 -0
- plyllm-1.0.0/.env.example +28 -0
- plyllm-1.0.0/.github/workflows/ci.yml +85 -0
- plyllm-1.0.0/.github/workflows/publish.yml +57 -0
- plyllm-1.0.0/.gitignore +220 -0
- plyllm-1.0.0/.prettierignore +6 -0
- plyllm-1.0.0/.prettierrc +4 -0
- plyllm-1.0.0/.python-version +1 -0
- plyllm-1.0.0/CHANGELOG.md +31 -0
- plyllm-1.0.0/LICENSE +21 -0
- plyllm-1.0.0/Makefile +62 -0
- plyllm-1.0.0/PKG-INFO +225 -0
- plyllm-1.0.0/README.md +179 -0
- plyllm-1.0.0/pyproject.toml +95 -0
- plyllm-1.0.0/scripts/release.py +122 -0
- plyllm-1.0.0/src/plyllm/__init__.py +66 -0
- plyllm-1.0.0/src/plyllm/client.py +221 -0
- plyllm-1.0.0/src/plyllm/concurrency.py +55 -0
- plyllm-1.0.0/src/plyllm/errors.py +77 -0
- plyllm-1.0.0/src/plyllm/llm_config.py +151 -0
- plyllm-1.0.0/src/plyllm/message.py +27 -0
- plyllm-1.0.0/src/plyllm/model.py +192 -0
- plyllm-1.0.0/src/plyllm/model_config.py +73 -0
- plyllm-1.0.0/src/plyllm/options.py +14 -0
- plyllm-1.0.0/src/plyllm/providers/__init__.py +0 -0
- plyllm-1.0.0/src/plyllm/providers/claude/__init__.py +0 -0
- plyllm-1.0.0/src/plyllm/providers/claude/client.py +123 -0
- plyllm-1.0.0/src/plyllm/providers/claude/models.py +190 -0
- plyllm-1.0.0/src/plyllm/providers/gemini/__init__.py +0 -0
- plyllm-1.0.0/src/plyllm/providers/gemini/client.py +186 -0
- plyllm-1.0.0/src/plyllm/providers/gemini/models.py +254 -0
- plyllm-1.0.0/src/plyllm/providers/grok/__init__.py +0 -0
- plyllm-1.0.0/src/plyllm/providers/grok/client.py +126 -0
- plyllm-1.0.0/src/plyllm/providers/grok/models.py +80 -0
- plyllm-1.0.0/src/plyllm/providers/huggingface/__init__.py +0 -0
- plyllm-1.0.0/src/plyllm/providers/huggingface/client.py +134 -0
- plyllm-1.0.0/src/plyllm/providers/huggingface/models.py +28 -0
- plyllm-1.0.0/src/plyllm/providers/metacentrum/__init__.py +0 -0
- plyllm-1.0.0/src/plyllm/providers/metacentrum/client.py +186 -0
- plyllm-1.0.0/src/plyllm/providers/metacentrum/models.py +103 -0
- plyllm-1.0.0/src/plyllm/providers/mistral/__init__.py +0 -0
- plyllm-1.0.0/src/plyllm/providers/mistral/client.py +144 -0
- plyllm-1.0.0/src/plyllm/providers/mistral/models.py +74 -0
- plyllm-1.0.0/src/plyllm/providers/openai/__init__.py +0 -0
- plyllm-1.0.0/src/plyllm/providers/openai/client.py +150 -0
- plyllm-1.0.0/src/plyllm/providers/openai/models.py +163 -0
- plyllm-1.0.0/src/plyllm/providers/providers.py +11 -0
- plyllm-1.0.0/src/plyllm/py.typed +0 -0
- plyllm-1.0.0/src/plyllm/rate_limit.py +165 -0
- plyllm-1.0.0/src/plyllm/response.py +84 -0
- plyllm-1.0.0/src/plyllm/router.py +119 -0
- plyllm-1.0.0/src/plyllm/tokens.py +33 -0
- plyllm-1.0.0/src/plyllm/utils.py +34 -0
- plyllm-1.0.0/tests/conftest.py +42 -0
- plyllm-1.0.0/tests/test_claude_provider.py +188 -0
- plyllm-1.0.0/tests/test_gemini_embeddings.py +154 -0
- plyllm-1.0.0/tests/test_huggingface_provider.py +84 -0
- plyllm-1.0.0/tests/test_llm_clients.py +127 -0
- plyllm-1.0.0/tests/test_llm_costs.py +73 -0
- plyllm-1.0.0/tests/test_llm_errors.py +271 -0
- plyllm-1.0.0/tests/test_llm_live.py +180 -0
- plyllm-1.0.0/tests/test_llm_options.py +85 -0
- plyllm-1.0.0/tests/test_llm_router.py +252 -0
- plyllm-1.0.0/tests/test_metacentrum_provider.py +140 -0
- plyllm-1.0.0/tests/test_mistral_provider.py +249 -0
- plyllm-1.0.0/tests/test_openai_embeddings.py +69 -0
- plyllm-1.0.0/tests/test_rate_limit.py +170 -0
- plyllm-1.0.0/tests/test_retired_models.py +70 -0
- plyllm-1.0.0/tests/test_served_models.py +94 -0
- plyllm-1.0.0/tests/test_structured_output.py +247 -0
- plyllm-1.0.0/uv.lock +2072 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: update-models
|
|
3
|
+
description: Update supported model definitions using current official provider documentation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Read `references/sources.md` before step 2. For every provider it gives the exact official
|
|
7
|
+
documentation URLs to fetch, the repository's conventions for that provider (which pricing tier,
|
|
8
|
+
the cache-read multiplier, how thinking and effort are encoded), and the places where a provider's
|
|
9
|
+
own pages contradict each other. It is a shortcut past the search, not a substitute for the docs:
|
|
10
|
+
always re-fetch the pages and verify against them. Update `references/sources.md` in the same change
|
|
11
|
+
whenever a URL moves, a provider is added, or a convention changes.
|
|
12
|
+
|
|
13
|
+
For every model provider supported by this repository:
|
|
14
|
+
|
|
15
|
+
1. Inspect the repository to find the provider's model definitions and related metadata.
|
|
16
|
+
2. Check the provider's official developer documentation for:
|
|
17
|
+
- current API model IDs
|
|
18
|
+
- pricing in USD
|
|
19
|
+
- reasoning/thinking support and available settings
|
|
20
|
+
- deprecated, renamed, or newly available models
|
|
21
|
+
|
|
22
|
+
3. Update the repository with the latest verified information.
|
|
23
|
+
- Preserve existing data structures, naming conventions, and pricing units.
|
|
24
|
+
- Add new supported models and update outdated entries.
|
|
25
|
+
- Remove or deprecate models only when appropriate for the repository.
|
|
26
|
+
- Do not guess undocumented pricing or capabilities.
|
|
27
|
+
|
|
28
|
+
4. Update related aliases, tests, fixtures, or documentation when required.
|
|
29
|
+
5. Run the relevant tests, formatter, and type checks. Then run `make test-models`: it is free (each
|
|
30
|
+
provider's model-list endpoint, keys from .env) and compares every active registry model with the
|
|
31
|
+
ids the provider serves right now. Read a failure before changing anything:
|
|
32
|
+
- `no longer serves [...]`: first check whether the id is an alias the listing omits (Anthropic
|
|
33
|
+
lists only the dated snapshot of some models, e.g. `claude-haiku-4-5-20251001`). If so, switch
|
|
34
|
+
`model_string` to the listed id the docs say it resolves to. Mark a model `retired=True` only
|
|
35
|
+
when the docs or the listing show it is really gone; never delete it, recorded results need it.
|
|
36
|
+
- An error raised while listing (401, 403, "used all available credits", a timeout) is an
|
|
37
|
+
account or network problem, not a registry one. Report it and leave the registry alone.
|
|
38
|
+
- A warning that a retired model is served again: report it; do not un-retire on your own.
|
|
39
|
+
|
|
40
|
+
`make test-models` only checks models already in the registry; it cannot see one that was left
|
|
41
|
+
out. `references/sources.md` lists models that were removed or skipped because the account could
|
|
42
|
+
not use them (e.g. grok-4.7 / grok-4.6), each with a free command to check whether they are served
|
|
43
|
+
now. Run those checks too, and re-add a model as soon as it is served.
|
|
44
|
+
|
|
45
|
+
6. Summarize:
|
|
46
|
+
- providers checked
|
|
47
|
+
- models added, updated, or removed
|
|
48
|
+
- pricing or reasoning changes
|
|
49
|
+
- validation performed, including the `make test-models` result per provider
|
|
50
|
+
- official documentation used
|
|
51
|
+
|
|
52
|
+
Use official provider documentation as the source of truth. Research every provider supported by the repository, not only providers such as OpenAI, Gemini, or Anthropic.
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Provider documentation sources
|
|
2
|
+
|
|
3
|
+
Where the model data in `src/plyllm/providers/*/models.py` comes from. Verified 2026-10-09.
|
|
4
|
+
|
|
5
|
+
Fetch the pricing page and the per-model pages in parallel; the pricing pages carry every model's
|
|
6
|
+
rates in one table, and the per-model pages are the only place that lists a model's exact set of
|
|
7
|
+
reasoning/effort levels. A general "reasoning" guide and a per-model page sometimes disagree about
|
|
8
|
+
which levels a model accepts — prefer the intersection, because an unsupported level is a runtime
|
|
9
|
+
400 while a missing one only blocks a config.
|
|
10
|
+
|
|
11
|
+
Batch prices (checked 2026-10-10) are a `batch_ratio` on each model, applied to input, cached input and
|
|
12
|
+
output: OpenAI 0.5 (every Batch row is half the Standard row; gpt-5.4 cached shows $0.13, rounded), Claude
|
|
13
|
+
0.5 (stacks with the cache multipliers), Mistral 0.5 (whether it covers cached input is not stated),
|
|
14
|
+
Gemini 0.5 (see its section for cache reads), Grok per model (0.8 on grok-4.3 and grok-4.20; no discount
|
|
15
|
+
listed for the others, which get `None`), Metacentrum `None` (free). Batch: OpenAI
|
|
16
|
+
<https://developers.openai.com/api/docs/guides/batch>, Anthropic pricing page, Gemini
|
|
17
|
+
<https://ai.google.dev/gemini-api/docs/batch-mode>, xAI <https://docs.x.ai/developers/pricing>, Mistral
|
|
18
|
+
<https://mistral.ai/pricing>.
|
|
19
|
+
|
|
20
|
+
## Anthropic (`claude`)
|
|
21
|
+
|
|
22
|
+
- Pricing (input / output / cache read + write, batch, all models): <https://platform.claude.com/docs/en/about-claude/pricing>
|
|
23
|
+
- Model lineup, IDs, context, max output, thinking mode, default effort: <https://platform.claude.com/docs/en/about-claude/models/overview>
|
|
24
|
+
- Effort levels per model, and per-model recommendations: <https://platform.claude.com/docs/en/build-with-claude/effort>
|
|
25
|
+
- One model's full spec: `https://platform.claude.com/docs/en/models/<slug>/overview` (e.g. `opus-5-5`, `fable-5-1`)
|
|
26
|
+
- Deprecations / retirements: <https://platform.claude.com/docs/en/about-claude/model-deprecations>
|
|
27
|
+
|
|
28
|
+
`docs.claude.com/en/docs/...` 302-redirects to `platform.claude.com/docs/en/...`; fetch the
|
|
29
|
+
`platform.claude.com` URL directly to save a round trip.
|
|
30
|
+
|
|
31
|
+
Repository notes: `cache_write_cost` is the 5-minute cache-write rate, 1.25x input on every model
|
|
32
|
+
(`_CACHE_WRITE_RATIO`); the client caches the system prompt with the default 5-minute lifetime, so the
|
|
33
|
+
1-hour rate (2x) is not stored. `cached_input_cost` is the cache-read rate, normally 0.1x input but 0.025x on
|
|
34
|
+
Fable 5.1 and 0.05x on Opus 5.5 and Sonnet 5.5. `cache_write_cost` is the 5-minute cache-write rate,
|
|
35
|
+
1.25x input on every model (`_CACHE_WRITE_RATIO`); the client caches the system prompt with the default
|
|
36
|
+
5-minute lifetime, so the 1-hour rate (2x) is never charged. The pricing page says the batch discount
|
|
37
|
+
stacks with both cache multipliers. The models endpoint lists some older models only by dated snapshot (`claude-haiku-4-5-20251001`,
|
|
38
|
+
`claude-opus-4-5-20251101`, `claude-sonnet-4-5-20250929`), not by alias, so use the snapshot there. Haiku 5.5 has two price tiers (prompts up to / over
|
|
39
|
+
100k tokens); the repository stores the lower one. "Adaptive (always on)" in the docs maps to `thinking_only=True`.
|
|
40
|
+
Models whose docs say thinking cannot be disabled at any effort are `thinking_only`; Opus 5 can only
|
|
41
|
+
disable it at `high` or below, which `_THINKING_ONLY_EFFORTS` already encodes. Sonnet 5.5 rejects
|
|
42
|
+
`"disabled"` and only offers `thinking: {"type": "between_tools"}` (effort `high` or below), which the
|
|
43
|
+
harness does not model, so it is `thinking_only`. The per-model thinking table ("Rejected with 400")
|
|
44
|
+
is at <https://platform.claude.com/docs/en/build-with-claude/thinking-troubleshooting>. Its footnote
|
|
45
|
+
names only Opus 5 and Haiku 5.5 as rejecting `"disabled"` at `xhigh`/`max`; the repository applies
|
|
46
|
+
that rule to every Claude model (`_THINKING_ONLY_EFFORTS`), which the docs neither confirm nor rule
|
|
47
|
+
out for Opus 4.8, Sonnet 5 and Sonnet 4.6.
|
|
48
|
+
|
|
49
|
+
## OpenAI (`openai`)
|
|
50
|
+
|
|
51
|
+
- Pricing, all models, input / cached input / output: <https://developers.openai.com/api/docs/pricing>
|
|
52
|
+
- Current model list: <https://developers.openai.com/api/docs/models>
|
|
53
|
+
- Per-model effort values, default and pricing: `https://developers.openai.com/api/docs/models/<model-id>`
|
|
54
|
+
- Reasoning guide (general, does not enumerate per model): <https://developers.openai.com/api/docs/guides/reasoning>
|
|
55
|
+
|
|
56
|
+
`platform.openai.com/docs/...` 301-redirects to `developers.openai.com/api/docs/...`.
|
|
57
|
+
|
|
58
|
+
Repository notes: the per-model pages list `none` as an effort value; the harness expresses that as
|
|
59
|
+
`thinking_enabled=False` (the provider sends effort `none`), so `none` is never in a `supported_reasoning`
|
|
60
|
+
set. A model whose page does not list `none` is `thinking_only` (on 2026-10-09: gpt-6-astra, gpt-6.1-sol,
|
|
61
|
+
the Pro models, and gpt-5-mini / gpt-5-nano, whose pages list no efforts at all). A model whose default
|
|
62
|
+
effort is `none` (gpt-5.4, -mini, -nano) is `needs_effort_to_think`, or thinking on would not reason.
|
|
63
|
+
Temperature is accepted only at effort `none` (GPT-6 migration guide, "When reasoning effort is not none,
|
|
64
|
+
remove temperature, top_p"), which `OpenAILLMModel` derives from `thinking_only`. Effort sets differ
|
|
65
|
+
within a generation — GPT-6.x and GPT-5.6 accept `max`, GPT-5.5 and GPT-5.4 stop at `xhigh`, and the
|
|
66
|
+
Pro variants start at `medium`. Cached input is not always 0.1x: gpt-6.1-sol is 0.05x. gpt-6.1-sol rejects `none` (thinking cannot be
|
|
67
|
+
turned off), and the gpt-6-astra page does not list `none`. gpt-5.6-sol is on promotional pricing "at
|
|
68
|
+
least through November 21, 2026". The gpt-5-mini / gpt-5-nano pages no longer list effort values.
|
|
69
|
+
Shutdown dates: <https://developers.openai.com/api/docs/deprecations>.
|
|
70
|
+
|
|
71
|
+
## Google Gemini (`gemini`)
|
|
72
|
+
|
|
73
|
+
- Pricing, paid tier, per model: <https://ai.google.dev/gemini-api/docs/pricing>
|
|
74
|
+
|
|
75
|
+
`cached_input_cost` is the "Context caching price" row (hourly storage is not modelled). The batch table
|
|
76
|
+
lists its own cache-read price; where it is not half of the standard one (2.5 models, 3.1 Pro, 3 Flash:
|
|
77
|
+
same as standard; 3.5 Flash-Lite: 0.02), record it as `batch_cached_input_cost`. The batch-mode page says
|
|
78
|
+
batch cache hits pay "the standard context caching rates", which contradicts the table for 3.8 / 3.6 Flash
|
|
79
|
+
and 3.1 Flash-Lite; the table is used. 3.7 Flash and 3.5 Flash had no pricing block on 2026-10-10, so
|
|
80
|
+
their cache price is 0 until the page lists one.
|
|
81
|
+
|
|
82
|
+
- Model list, stable vs preview: <https://ai.google.dev/gemini-api/docs/models>
|
|
83
|
+
- Deprecations and shutdown dates: <https://ai.google.dev/gemini-api/docs/deprecations>
|
|
84
|
+
- Embedding task prefixes (`task:` / `title:`), verbatim: <https://ai.google.dev/gemini-api/docs/embeddings>
|
|
85
|
+
|
|
86
|
+
Repository notes: Flash pricing is promotional and dated — 3.8 / 3.7 / 3.6 Flash are $0.75 / $3.75
|
|
87
|
+
through 2026-12-31 and $1.50 / $7.50 after. Record the rate in force, with a comment giving the
|
|
88
|
+
change date. Pro models have a >200k-token tier; the repository stores the standard tier only.
|
|
89
|
+
The models page lists preview models that the deprecations page has already shut down — always
|
|
90
|
+
cross-check both before keeping a `-preview` id (on 2026-10-09 it still listed
|
|
91
|
+
`gemini-embedding-2-preview`, which shut down on 2026-08-10). The thinking guide
|
|
92
|
+
(<https://ai.google.dev/gemini-api/docs/thinking>) is the per-model table of thinking levels and
|
|
93
|
+
defaults; it no longer documents `thinking_budget`, which the 2.5 models still use here. 3.8 and 3.7
|
|
94
|
+
Flash reject `minimal`. 3.1 Flash-Lite is not in that table.
|
|
95
|
+
|
|
96
|
+
## xAI Grok (`grok`)
|
|
97
|
+
|
|
98
|
+
- Model list and pricing table: <https://docs.x.ai/docs/models>
|
|
99
|
+
- Per-model page, with the effort list and default: `https://docs.x.ai/docs/models/<model-id>`
|
|
100
|
+
- Reasoning guide: <https://docs.x.ai/docs/guides/reasoning>
|
|
101
|
+
|
|
102
|
+
Repository notes: prices are the standard sub-200k-token tier; requests reaching 200k are billed at
|
|
103
|
+
roughly double for the whole request. The reasoning guide and the per-model pages disagree about
|
|
104
|
+
`xhigh` on grok-4.5 (the guide says it is silently treated as `high`) and about whether grok-4.3
|
|
105
|
+
reasons at all — both are kept at low/medium/high. The guide documents `reasoning_effort` only for
|
|
106
|
+
grok-4.7 / 4.6 / 4.5, and the grok-4.20 page has no effort section, so grok-4.20 takes none.
|
|
107
|
+
Reasoning cannot be disabled on grok-4.7 / 4.6 / 4.5, so they are `thinking_only`, as is grok-4.20
|
|
108
|
+
(no effort section; it always reasons); grok-4.3 accepts `none` (default `low`), which the provider sends
|
|
109
|
+
when thinking is off. No page documents `temperature` for the reasoning models, so every Grok model has
|
|
110
|
+
`TemperatureSupport.NEVER`; nor is `reasoning.summary` documented (it is still sent with an effort). Retirements: <https://docs.x.ai/developers/migration/may-15-retirement>.
|
|
111
|
+
|
|
112
|
+
**Waiting to be re-added: grok-4.7 and grok-4.6.** Removed on 2026-10-09: the docs list them, but the
|
|
113
|
+
API returns 404 for this account ("does not exist or your team does not have access to it"). On every
|
|
114
|
+
update, check whether they are served now (free, needs `GROK_API_KEY`):
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
uv run python -c "import asyncio; from plyllm import LLM, LLMConfig, Provider; print(sorted(asyncio.run(LLM(LLMConfig.from_env()).served_models(Provider.GROK))))"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
If the list contains them, add them back with the values from their model pages (on 2026-10-09:
|
|
121
|
+
$2.00 / $6.00, cached $0.50; effort low/medium/high/xhigh, default high; reasoning cannot be
|
|
122
|
+
disabled), run `make test-models`, and delete this paragraph.
|
|
123
|
+
|
|
124
|
+
## Mistral (`mistral`)
|
|
125
|
+
|
|
126
|
+
- API pricing per model: <https://docs.mistral.ai/inference/pricing> (`mistral.ai/pricing/api` 301-redirects here)
|
|
127
|
+
- One model's page, with its API id: `https://docs.mistral.ai/models/<slug>` (e.g. `mistral-large-4-0`,
|
|
128
|
+
`mistral-medium-3-5-26-04`, `mistral-small-4-0-26-03`; the undated slugs of the last two 404)
|
|
129
|
+
- Model list, dated ids, deprecations: <https://docs.mistral.ai/getting-started/models/models_overview/>
|
|
130
|
+
- Reasoning capability: <https://docs.mistral.ai/capabilities/reasoning/>
|
|
131
|
+
|
|
132
|
+
Repository notes: docs slugs are not API ids — the model page shows the id next to the parameter
|
|
133
|
+
count (`mistral-large-2512 +1` for Large 3, `mistral-large-4 +1` for Large 4). The reasoning page
|
|
134
|
+
lists which models take `reasoning_effort` (Small, Medium 3.5, Large 4, hosted GLM 5.3), but only
|
|
135
|
+
documents `high` and `none`, so `_MISTRAL_REASONING` is `{high}` (`none` is what the provider sends
|
|
136
|
+
when thinking is off). Do not take the set from the SDK enum (`none, minimal, low, medium, high,
|
|
137
|
+
xhigh`): on 2026-10-09 `mistral-small-2603` rejected `low` with "Must be one of (none, high)".
|
|
138
|
+
Widen the set for a model only after `make test-live` (or a single paid call, approved first) shows
|
|
139
|
+
the API accepts the level. Cached input is documented only as "up to -90% on input tokens", so `cached_input_cost` is
|
|
140
|
+
0.1x input. Large 4 is on an undated sale; the repository records the sale price
|
|
141
|
+
with the list price in a comment. Large 3 and Ministral 3 are deliberately not in the repository
|
|
142
|
+
because their reasoning support is undocumented. Hosted GLM 5.3 (`zai-glm-5-3`) documents only
|
|
143
|
+
`low`/`high`/`max` and rejects `none`, which the provider sends when thinking is off; it is left out
|
|
144
|
+
until the provider can express that. The Medium 3.5 page lists `mistral-medium-3-5`,
|
|
145
|
+
`mistral-medium-3` and `mistral-medium-latest`, not the `mistral-medium-2604` the repository sends;
|
|
146
|
+
`make test-models` shows whether the endpoint still serves it. WebFetch has swapped price labels on
|
|
147
|
+
the Large 4 page; read the pricing table from raw HTML (`curl`) when in doubt.
|
|
148
|
+
|
|
149
|
+
## Metacentrum / e-INFRA (`metacentrum`)
|
|
150
|
+
|
|
151
|
+
- Served models, API ids, aliases, obsolete models: <https://docs.cerit.io/en/docs/ai-as-a-service/chat-ai>
|
|
152
|
+
- API base URL: `https://llm.ai.e-infra.cz/v1/` (set locally through `METACENTRUM_BASE_URL` / `METACENTRUM_API_KEY`)
|
|
153
|
+
|
|
154
|
+
Repository notes: self-hosted and free, so every model carries `input_cost=0` / `output_cost=0`.
|
|
155
|
+
The service replaces models without notice and the page has a separate "Obsolete Models" table —
|
|
156
|
+
check it, not just the main table. The page can lag the live endpoint (on 2026-10-07 it still read
|
|
157
|
+
"Effective June 30, 2026" and listed `deepseek-v4-flash`, while `/v1/models` served
|
|
158
|
+
`deepseek-v4.1-flash` from 2026-09-28). Prefer `GET https://llm.ai.e-infra.cz/v1/models` (free,
|
|
159
|
+
needs `METACENTRUM_API_KEY`) and ask before calling it. Models it drops are **kept** in `models.py`
|
|
160
|
+
and marked `retired=True`, so existing experiment configs and recorded results still resolve and
|
|
161
|
+
cost (`resolve_model` raises on an unknown name) while new calls are refused. `make test-models`
|
|
162
|
+
(free, needs the keys in .env) compares every provider's active registry models with its served list
|
|
163
|
+
and fails on a mismatch. Reasoning-effort support is not documented per model, so leave
|
|
164
|
+
`supported_reasoning=None` unless a model is known to accept it; the only documented toggle is
|
|
165
|
+
DeepSeek's `chat_template_kwargs: {"thinking": true}` (reasoning is off by default), on
|
|
166
|
+
<https://docs.cerit.io/en/docs/ai-as-a-service/ai-api>. That page is out of date: on 2026-10-09
|
|
167
|
+
deepseek-v4.1-flash reasoned with and without it, and with `thinking: false`. The thinking flags come
|
|
168
|
+
from a free probe instead (one short prompt per model, thinking on and off, reasoning tokens counted; put
|
|
169
|
+
a unique id in each prompt, because the proxy caches identical prompts and ignores `extra_body` when it
|
|
170
|
+
does). Results on 2026-10-09: the Qwen templates read `enable_thinking` (not `thinking`), kimi-k3 reads
|
|
171
|
+
`thinking`, gpt-oss-120b and deepseek-v4.1-flash always reason (`thinking_only`), gemma-4 reasons only with
|
|
172
|
+
an effort (`needs_effort_to_think`), mistral-medium-3.5 gave no reasoning with thinking on
|
|
173
|
+
(`thinking=False`), and glm-5.3 answered every call with 400 "`tools` must not be an empty array". On
|
|
174
|
+
2026-10-10 that happened even for a minimal `/v1/responses` request, while `/v1/chat/completions` answered
|
|
175
|
+
normally: the proxy's Responses-to-chat conversion breaks for this model, not the harness's request. The stable aliases (`qwen3.5`, `kimi`,
|
|
176
|
+
`glm`, ...) resolve to whatever is current, so they keep configs working but not outputs: `qwen3.5`
|
|
177
|
+
points at `qwen3.5-int4` (397B, AWQ int4).
|
|
178
|
+
|
|
179
|
+
## Hugging Face (`huggingface`)
|
|
180
|
+
|
|
181
|
+
Local encoder checkpoints run through `transformers`; they are free, have no API pricing and no
|
|
182
|
+
reasoning settings. Only verify the repo id still exists, e.g.
|
|
183
|
+
<https://huggingface.co/princeton-nlp/sup-simcse-bert-base-uncased>.
|
|
184
|
+
|
|
185
|
+
## After editing
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
make format
|
|
189
|
+
make test
|
|
190
|
+
make typecheck
|
|
191
|
+
make test-models # free: are all active models still served? Needs the keys in .env
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Then check the prices and effort sets round-trip:
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
uv run python -c "from plyllm.providers.claude.models import claude_models; print([(m.model_name, m.input_cost, m.output_cost) for m in claude_models()])"
|
|
198
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# openai configuration
|
|
2
|
+
OPENAI_ORGANIZATION=
|
|
3
|
+
OPENAI_PROJECT=
|
|
4
|
+
OPENAI_API_KEY=
|
|
5
|
+
|
|
6
|
+
# gemini configuration
|
|
7
|
+
GEMINI_PROJECT=
|
|
8
|
+
GEMINI_API_KEY=
|
|
9
|
+
|
|
10
|
+
# grok configuration
|
|
11
|
+
GROK_API_KEY=
|
|
12
|
+
GROK_BASE_URL=
|
|
13
|
+
|
|
14
|
+
# claude configuration
|
|
15
|
+
CLAUDE_API_KEY=
|
|
16
|
+
|
|
17
|
+
# mistral configuration
|
|
18
|
+
MISTRAL_API_KEY=
|
|
19
|
+
|
|
20
|
+
# metacentrum configuration
|
|
21
|
+
METACENTRUM_BASE_URL=
|
|
22
|
+
METACENTRUM_API_KEY=
|
|
23
|
+
|
|
24
|
+
# huggingface configuration (only needed for gated/private models)
|
|
25
|
+
HF_TOKEN=
|
|
26
|
+
|
|
27
|
+
# requests that share this key and a prompt prefix reuse the provider cache (OpenAI, Grok, Mistral); default PlyLLM
|
|
28
|
+
PROMPT_CACHE_KEY=
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ci-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v5
|
|
20
|
+
with:
|
|
21
|
+
enable-cache: true
|
|
22
|
+
|
|
23
|
+
- name: Sync dependencies
|
|
24
|
+
run: uv sync --group dev --python 3.13
|
|
25
|
+
|
|
26
|
+
- name: Ruff lint
|
|
27
|
+
run: uv run ruff check .
|
|
28
|
+
|
|
29
|
+
- name: Ruff format check
|
|
30
|
+
run: uv run ruff format --check .
|
|
31
|
+
|
|
32
|
+
- name: Prettier format check
|
|
33
|
+
run: npx --yes prettier@3.9.6 --check .
|
|
34
|
+
|
|
35
|
+
typecheck:
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
|
|
40
|
+
- name: Install uv
|
|
41
|
+
uses: astral-sh/setup-uv@v5
|
|
42
|
+
with:
|
|
43
|
+
enable-cache: true
|
|
44
|
+
|
|
45
|
+
- name: Sync dependencies
|
|
46
|
+
run: uv sync --group dev --python 3.13
|
|
47
|
+
|
|
48
|
+
- name: Pyright
|
|
49
|
+
run: make typecheck
|
|
50
|
+
|
|
51
|
+
test:
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
strategy:
|
|
54
|
+
fail-fast: false
|
|
55
|
+
matrix:
|
|
56
|
+
python-version: ["3.12", "3.13"]
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
|
|
60
|
+
- name: Install uv
|
|
61
|
+
uses: astral-sh/setup-uv@v5
|
|
62
|
+
with:
|
|
63
|
+
enable-cache: true
|
|
64
|
+
|
|
65
|
+
- name: Sync dependencies
|
|
66
|
+
run: uv sync --group dev --python ${{ matrix.python-version }}
|
|
67
|
+
|
|
68
|
+
- name: Run tests
|
|
69
|
+
run: uv run --group dev --python ${{ matrix.python-version }} pytest -q
|
|
70
|
+
|
|
71
|
+
build:
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
steps:
|
|
74
|
+
- uses: actions/checkout@v4
|
|
75
|
+
|
|
76
|
+
- name: Install uv
|
|
77
|
+
uses: astral-sh/setup-uv@v5
|
|
78
|
+
with:
|
|
79
|
+
enable-cache: true
|
|
80
|
+
|
|
81
|
+
- name: Build wheel and sdist
|
|
82
|
+
run: uv build
|
|
83
|
+
|
|
84
|
+
- name: Check package metadata
|
|
85
|
+
run: uvx twine check dist/*
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI when a GitHub Release is published (see scripts/release.py).
|
|
4
|
+
# Uses PyPI Trusted Publishing (OIDC) — no API token is stored.
|
|
5
|
+
# One-time setup on PyPI: add a trusted publisher for project "plyllm" with
|
|
6
|
+
# owner=radajakub, repository=plyllm, workflow=publish.yml, environment=pypi.
|
|
7
|
+
on:
|
|
8
|
+
release:
|
|
9
|
+
types: [published]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@v5
|
|
19
|
+
|
|
20
|
+
- name: Verify tag matches the package version
|
|
21
|
+
run: |
|
|
22
|
+
version="$(uv version --short)"
|
|
23
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
24
|
+
if [ "$version" != "$tag" ]; then
|
|
25
|
+
echo "tag $GITHUB_REF_NAME does not match pyproject version $version" >&2
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
- name: Build wheel and sdist
|
|
30
|
+
run: uv build
|
|
31
|
+
|
|
32
|
+
- name: Check package metadata
|
|
33
|
+
run: uvx twine check dist/*
|
|
34
|
+
|
|
35
|
+
- name: Upload build artifacts
|
|
36
|
+
uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist
|
|
39
|
+
path: dist/
|
|
40
|
+
|
|
41
|
+
publish:
|
|
42
|
+
needs: build
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
environment:
|
|
45
|
+
name: pypi
|
|
46
|
+
url: https://pypi.org/p/plyllm
|
|
47
|
+
permissions:
|
|
48
|
+
id-token: write # required for trusted publishing
|
|
49
|
+
steps:
|
|
50
|
+
- name: Download build artifacts
|
|
51
|
+
uses: actions/download-artifact@v4
|
|
52
|
+
with:
|
|
53
|
+
name: dist
|
|
54
|
+
path: dist/
|
|
55
|
+
|
|
56
|
+
- name: Publish to PyPI
|
|
57
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
plyllm-1.0.0/.gitignore
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
*.lcov
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
# Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# uv
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
100
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
101
|
+
# commonly ignored for libraries.
|
|
102
|
+
# uv.lock
|
|
103
|
+
|
|
104
|
+
# poetry
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
106
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
107
|
+
# commonly ignored for libraries.
|
|
108
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
109
|
+
# poetry.lock
|
|
110
|
+
# poetry.toml
|
|
111
|
+
|
|
112
|
+
# pdm
|
|
113
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
114
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
115
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
116
|
+
# pdm.lock
|
|
117
|
+
# pdm.toml
|
|
118
|
+
.pdm-python
|
|
119
|
+
.pdm-build/
|
|
120
|
+
|
|
121
|
+
# pixi
|
|
122
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
123
|
+
# pixi.lock
|
|
124
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
125
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
126
|
+
.pixi/*
|
|
127
|
+
!.pixi/config.toml
|
|
128
|
+
|
|
129
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
130
|
+
__pypackages__/
|
|
131
|
+
|
|
132
|
+
# Celery stuff
|
|
133
|
+
celerybeat-schedule*
|
|
134
|
+
celerybeat.pid
|
|
135
|
+
|
|
136
|
+
# Redis
|
|
137
|
+
*.rdb
|
|
138
|
+
*.aof
|
|
139
|
+
*.pid
|
|
140
|
+
|
|
141
|
+
# RabbitMQ
|
|
142
|
+
mnesia/
|
|
143
|
+
rabbitmq/
|
|
144
|
+
rabbitmq-data/
|
|
145
|
+
|
|
146
|
+
# ActiveMQ
|
|
147
|
+
activemq-data/
|
|
148
|
+
|
|
149
|
+
# SageMath parsed files
|
|
150
|
+
*.sage.py
|
|
151
|
+
|
|
152
|
+
# Environments
|
|
153
|
+
.env
|
|
154
|
+
.envrc
|
|
155
|
+
.venv
|
|
156
|
+
env/
|
|
157
|
+
venv/
|
|
158
|
+
ENV/
|
|
159
|
+
env.bak/
|
|
160
|
+
venv.bak/
|
|
161
|
+
|
|
162
|
+
# Spyder project settings
|
|
163
|
+
.spyderproject
|
|
164
|
+
.spyproject
|
|
165
|
+
|
|
166
|
+
# Rope project settings
|
|
167
|
+
.ropeproject
|
|
168
|
+
|
|
169
|
+
# mkdocs/Zensical documentation
|
|
170
|
+
/site
|
|
171
|
+
|
|
172
|
+
# mypy
|
|
173
|
+
.mypy_cache/
|
|
174
|
+
.dmypy.json
|
|
175
|
+
dmypy.json
|
|
176
|
+
|
|
177
|
+
# Pyre type checker
|
|
178
|
+
.pyre/
|
|
179
|
+
|
|
180
|
+
# pytype static type analyzer
|
|
181
|
+
.pytype/
|
|
182
|
+
|
|
183
|
+
# Cython debug symbols
|
|
184
|
+
cython_debug/
|
|
185
|
+
|
|
186
|
+
# PyCharm
|
|
187
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
188
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
189
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
190
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
191
|
+
# .idea/
|
|
192
|
+
|
|
193
|
+
# Abstra
|
|
194
|
+
# Abstra is an AI-powered process automation framework.
|
|
195
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
196
|
+
# Learn more at https://abstra.io/docs
|
|
197
|
+
.abstra/
|
|
198
|
+
|
|
199
|
+
# Visual Studio Code
|
|
200
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore that
|
|
201
|
+
# can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
202
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer, you
|
|
203
|
+
# could uncomment the following to ignore the entire vscode folder
|
|
204
|
+
# .vscode/
|
|
205
|
+
# Temporary file for partial code execution
|
|
206
|
+
tempCodeRunnerFile.py
|
|
207
|
+
|
|
208
|
+
# Ruff stuff:
|
|
209
|
+
.ruff_cache/
|
|
210
|
+
|
|
211
|
+
# PyPI configuration file
|
|
212
|
+
.pypirc
|
|
213
|
+
|
|
214
|
+
# Marimo
|
|
215
|
+
marimo/_static/
|
|
216
|
+
marimo/_lsp/
|
|
217
|
+
__marimo__/
|
|
218
|
+
|
|
219
|
+
# Streamlit
|
|
220
|
+
.streamlit/secrets.toml
|
plyllm-1.0.0/.prettierrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|