Metadata-Version: 2.4
Name: keycall
Version: 0.3.1
Summary: One consistent interface for validating AI-provider API keys, listing and filtering their models, and making normalized calls.
Project-URL: Homepage, https://github.com/shehuphd/keycall
Project-URL: Documentation, https://github.com/shehuphd/keycall/blob/main/USAGE.md
Project-URL: Changelog, https://github.com/shehuphd/keycall/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/shehuphd/keycall/issues
Project-URL: Source, https://github.com/shehuphd/keycall
Author: Mo Shehu
License-Expression: AGPL-3.0-or-later
License-File: LICENSE
Keywords: anthropic,api-key-validation,api-keys,gemini,llm,model-picker,openai
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: tomli>=2.0; python_version < '3.11'
Requires-Dist: typing-extensions>=4.0; python_version < '3.11'
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Requires-Dist: traceact>=0.13; extra == 'dev'
Provides-Extra: traceact
Requires-Dist: traceact>=0.13; extra == 'traceact'
Description-Content-Type: text/markdown

# KeyCall

One consistent interface for validating AI-provider API keys, listing and filtering the models available to them, and making normalized calls, so every product stops rebuilding the same model-picker filters and provider wrappers.

**Status: early release.** Key validation, model listing and filtering,
text generation, native web search with normalized citations, and structured
JSON output all work and are live-verified against every supported provider.
Streaming, general tool calling, and non-text modalities are not implemented
yet. The API is settled but may still shift before 1.0.

Docs: [USAGE.md](https://github.com/shehuphd/keycall/blob/main/USAGE.md) for the full API and CLI reference · [CHANGELOG.md](https://github.com/shehuphd/keycall/blob/main/CHANGELOG.md) for version history.

## Quick start

```python
from keycall import KeyCall, Message, ModelCategory, TextInput

with KeyCall(provider="openai", api_key=secret) as client:
    discovery = client.list_models(categories={ModelCategory.TEXT_GENERATION})

    result = client.generate_text(
        model=discovery.models[0].id,
        messages=[Message(role="user", content=[TextInput(text="Hello.")])],
    )

print(result.text)
print(result.usage.total_tokens)
print(result.round_trip_duration_ms)
```

- **Explicit provider, always.** KeyCall never guesses which vendor issued a key and never sends a credential to more than the one provider you name.
- **No credential storage.** Keys live in memory for the client's lifetime, wrapped in a redacting type that keeps them out of reprs, logs, traces, exceptions, and pickles. Your app decides how to store them.
- **Model filtering built in.** Text-generation models by default; embeddings, image, audio, and other categories on request; unknown models never silently enter the default picker.
- **Typed errors.** Invalid key, rate limit, provider outage, timeout, and malformed response are distinguishable, never collapsed into "invalid key."
- **Web search with citations.** `web_search=True` turns on the provider's native search tool (OpenAI, Anthropic, Gemini; Perplexity always searches) and returns sources normalized to one `Citation` shape.
- **Structured output.** `response_schema=<JSON Schema>` is enforced provider-side on OpenAI, Anthropic, Gemini, Moonshot, and Perplexity; on providers without enforcement (DeepSeek, unverified custom targets) KeyCall falls back to guaranteed-valid-JSON mode and adds a result warning rather than claiming a guarantee it can't back. `result.text` is always the JSON string, regardless of which mechanism produced it.
- **Hardened transport.** TLS always verified, redirects refused, response sizes capped, SSRF and DNS-rebinding guards on custom endpoints, and generation is never silently retried.

## Provider support

Live-verified 2026-08-05 (one model-list call plus one bounded generation per provider):

| Provider | Protocol | Listing | Generation |
|---|---|---|---|
| OpenAI | openai | verified | verified |
| Anthropic | anthropic | verified | verified |
| Google Gemini | gemini | verified | verified |
| DeepSeek | openai-compatible | verified | verified |
| Perplexity | openai-compatible | verified | verified |
| Moonshot/Kimi | openai-compatible | verified | verified |
| Custom endpoint (explicit `base_url`) | openai-compatible | fixtures only | fixtures only |

Two provider quirks worth knowing, both handled:

**Gemini** keeps retired models in its list endpoint (`gemini-2.5-flash` returns
"no longer available to new users") with no lifecycle field to pre-filter on,
and meters quota per model and tier, so one model's 429 says nothing about the
next. Its `supportedGenerationMethods` is also a transport signal rather than a
modality claim: TTS variants advertise `generateContent` and then refuse a text
response, so KeyCall lets a distinctive identifier modality outrank it.

**Perplexity**'s `GET /v1/models` is scoped to the Agent API and returns
vendor-prefixed router models (`anthropic/...`, `perplexity/sonar`) that the
Sonar route rejects. Sonar's own models are not API-discoverable, so KeyCall
maintains them in its catalog and uses the list call purely as a credential
check.

### Structured output notes, per provider

- **OpenAI** requires `additionalProperties: false` on every object level of
  the schema for its strict `json_schema` mode, or the request 400s. This is
  an OpenAI requirement, not a KeyCall one — write schemas with it from the
  start.
- **Anthropic** implements structured output by forcing a single synthetic
  tool call; it cannot be combined with `web_search=True` in the same
  request (forcing one tool prevents the model calling a different one), and
  KeyCall rejects that combination before any network call.
- **Gemini**'s equivalent combination (`web_search=True` with
  `response_schema`) is not gated — no live-verified evidence either way
  that Gemini rejects it, so KeyCall passes it through rather than guessing.
- **DeepSeek** hard-requires the literal word "json" somewhere in the prompt
  for its `json_object` fallback mode, or it 400s. KeyCall detects this and
  injects a short system instruction automatically when needed, and always
  says so via a result warning.
- **Moonshot/Kimi** reasoning-capable models can spend the entire
  `max_output_tokens` budget on a visible reasoning trace and never emit a
  final answer if the budget is too small. KeyCall detects the resulting
  empty-content-with-reasoning-trace response and adds a warning rather than
  returning a silent empty result; give these models a larger budget than
  you'd expect a short answer to need.

Because of quirks like these, `keycall verify --generate` walks the filtered
models in provider order and prints the outcome of every attempt until one
succeeds, so drift stays visible rather than being masked by a silent retry.

## Local viewer

```bash
keycall view --source ./keys.toml
```

Opens a token-protected local web app over your loaded targets: a dashboard
with live key checks, a sortable model browser with category filters, a
playground for real generation calls (web search included), and a verify
report. Keys never leave the server process and never appear in the browser.

Or double-click / run a launcher from a fresh clone — it creates the venv,
installs KeyCall, finds your key file, and starts the viewer:
`launch.command` (macOS), `launch.sh` (Linux/macOS), `launch.bat` (Windows).

## Verifying keys from the command line

```bash
keycall verify --source ./keys.toml
```

```bash
keycall verify --source ./keys.toml --generate
```

`--generate` also makes one small bounded call per target. Sources can be TXT,
JSON, or TOML, an explicit `env:VAR_NAME` reference, or an interactive prompt.
See `keycall-test-keys.example.toml` for the format and
[USAGE.md](https://github.com/shehuphd/keycall/blob/main/USAGE.md#the-verify-cli) for the full reference. Keys never appear
in output, and KeyCall never writes to or deletes your credential file.

## Installation

```bash
pip install keycall
```

## Development

```bash
pip install -e ".[dev]"
pytest
```

## Author

Built by [Mo Shehu](https://mohammedshehu.com).

## License

AGPL-3.0-or-later. See [LICENSE](LICENSE).
