Providers, Models, and Keys¤
Provider Inference¤
You never name a provider in a skill, only a model id. The provider is derived from the id:
| Model id prefix | Provider | Backend |
|---|---|---|
claude-* |
anthropic |
Anthropic SDK |
deepseek-* |
deepseek |
OpenAI SDK with https://api.deepseek.com |
gpt-*, o1/o3/o4-* |
openai |
OpenAI SDK |
gemini-* |
google |
provider slot exists; backend is not wired yet |
thinkingmachines/*, inkling* |
tinker |
OpenAI SDK text completions |
root("S").model("deepseek-chat") # DeepSeek
root("S").model("claude-opus-4-8") # Anthropic
DeepSeek is OpenAI-compatible, so it runs through the OpenAI SDK with a DeepSeek
base URL. Tinker base models use the OpenAI SDK's completions.create endpoint
with a plain-text prompt; they do not use chat.completions or function
calling. OpenAI-compatible chat calls use temperature=0 for stable structured
output.
Keys¤
Keys are resolved per provider, in order:
- Stored config at
~/.local/share/recon/auth.json({"<provider>": {"api_keys": [...]}}). - The provider's environment variable:
ANTHROPIC_API_KEY,DEEPSEEK_API_KEY,OPENAI_API_KEY,GOOGLE_API_KEY, orTINKER_API_KEY.
A .env in the working directory is loaded automatically via python-dotenv,
so adding DEEPSEEK_API_KEY=... to .env is enough. .env is gitignored; do
not commit keys.
Store keys explicitly with the CLI:
jdsl config add -p deepseek sk-...
jdsl config list
config list masks key values.
For Tinker, set TINKER_API_KEY and use a base model such as
thinkingmachines/Inkling:
root("classify").model("thinkingmachines/Inkling")
Tinker currently supports bounded text generation for JDSL predict leaves.
The base thinkingmachines/Inkling model is not instruction-tuned: it may
continue with unrelated text after a correct prefix, so it is not suitable for
exact branching or structured behavior contracts without a compatible
instruction-tuned model or provider-side constrained decoding. react leaves
require chat tool calling and fail explicitly for Tinker models.
Routing and Rotation¤
Each provider gets a RoundRobinRouter over its key list. On auth, permission,
or rate-limit errors, the router rotates to the next key and retries up to five
attempts.
Persistent per-key status tracking is not implemented yet.
Model Injection in Tests¤
Skills accept an explicit model object:
ctx = skill.run(model=fake_model("billing"), message="double charged")
The object only needs the LanguageModel shape used by the leaf: generate for
predict, and converse for react. The repository tests use this path to stay
offline.
Adding a Provider¤
- Add it to
SUPPORTED_PROVIDERS,ENV_KEYS, and, if OpenAI-compatible,BASE_URLSinjdsl/config.py. - Teach
provider_for_modelthe model-id prefix. - If it is not OpenAI-compatible, add a backend function in
jdsl/provider.pyand dispatch to it inLanguageModel.generateorLanguageModel.converse.