llmshim 0.1.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.
- llmshim-0.1.0/.github/workflows/publish-pypi.yml +129 -0
- llmshim-0.1.0/.gitignore +2 -0
- llmshim-0.1.0/CLAUDE.md +106 -0
- llmshim-0.1.0/Cargo.lock +1968 -0
- llmshim-0.1.0/Cargo.toml +52 -0
- llmshim-0.1.0/LICENSE +21 -0
- llmshim-0.1.0/PKG-INFO +152 -0
- llmshim-0.1.0/README.md +264 -0
- llmshim-0.1.0/examples/chat.rs +25 -0
- llmshim-0.1.0/examples/stream.rs +45 -0
- llmshim-0.1.0/llmshim/__init__.py +17 -0
- llmshim-0.1.0/llmshim/_client.py +246 -0
- llmshim-0.1.0/llmshim/_server.py +161 -0
- llmshim-0.1.0/pyproject.toml +34 -0
- llmshim-0.1.0/src/client.rs +172 -0
- llmshim-0.1.0/src/config.rs +106 -0
- llmshim-0.1.0/src/env.rs +13 -0
- llmshim-0.1.0/src/error.rs +27 -0
- llmshim-0.1.0/src/fallback.rs +177 -0
- llmshim-0.1.0/src/lib.rs +85 -0
- llmshim-0.1.0/src/log.rs +138 -0
- llmshim-0.1.0/src/main.rs +1251 -0
- llmshim-0.1.0/src/models.rs +72 -0
- llmshim-0.1.0/src/provider.rs +25 -0
- llmshim-0.1.0/src/providers/anthropic.rs +616 -0
- llmshim-0.1.0/src/providers/gemini.rs +537 -0
- llmshim-0.1.0/src/providers/mod.rs +4 -0
- llmshim-0.1.0/src/providers/openai.rs +356 -0
- llmshim-0.1.0/src/providers/xai.rs +306 -0
- llmshim-0.1.0/src/proxy/convert.rs +174 -0
- llmshim-0.1.0/src/proxy/error.rs +60 -0
- llmshim-0.1.0/src/proxy/handlers.rs +151 -0
- llmshim-0.1.0/src/proxy/mod.rs +28 -0
- llmshim-0.1.0/src/proxy/types.rs +167 -0
- llmshim-0.1.0/src/router.rs +106 -0
- llmshim-0.1.0/src/vision.rs +215 -0
- llmshim-0.1.0/tests/integration.rs +401 -0
- llmshim-0.1.0/tests/integration_fallback.rs +199 -0
- llmshim-0.1.0/tests/integration_gemini.rs +438 -0
- llmshim-0.1.0/tests/integration_long_context.rs +189 -0
- llmshim-0.1.0/tests/integration_multimodel.rs +376 -0
- llmshim-0.1.0/tests/integration_proxy.rs +409 -0
- llmshim-0.1.0/tests/integration_thinking.rs +332 -0
- llmshim-0.1.0/tests/integration_vision.rs +418 -0
- llmshim-0.1.0/tests/unit_anthropic.rs +1184 -0
- llmshim-0.1.0/tests/unit_fallback.rs +124 -0
- llmshim-0.1.0/tests/unit_gemini.rs +646 -0
- llmshim-0.1.0/tests/unit_log.rs +103 -0
- llmshim-0.1.0/tests/unit_models.rs +64 -0
- llmshim-0.1.0/tests/unit_multimodel.rs +295 -0
- llmshim-0.1.0/tests/unit_openai.rs +491 -0
- llmshim-0.1.0/tests/unit_proxy.rs +281 -0
- llmshim-0.1.0/tests/unit_proxy_convert.rs +195 -0
- llmshim-0.1.0/tests/unit_router.rs +258 -0
- llmshim-0.1.0/tests/unit_sse.rs +130 -0
- llmshim-0.1.0/tests/unit_vision.rs +370 -0
- llmshim-0.1.0/tests/unit_xai.rs +437 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
env:
|
|
10
|
+
PYTHON_VERSION: "3.12"
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
# --- Source distribution ---
|
|
14
|
+
sdist:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
21
|
+
- name: Build sdist
|
|
22
|
+
uses: PyO3/maturin-action@v1
|
|
23
|
+
with:
|
|
24
|
+
command: sdist
|
|
25
|
+
args: --out dist
|
|
26
|
+
working-directory: clients/python
|
|
27
|
+
- uses: actions/upload-artifact@v4
|
|
28
|
+
with:
|
|
29
|
+
name: wheels-sdist
|
|
30
|
+
path: clients/python/dist
|
|
31
|
+
|
|
32
|
+
# --- macOS (ARM64 + x86_64) ---
|
|
33
|
+
macos:
|
|
34
|
+
runs-on: macos-14
|
|
35
|
+
strategy:
|
|
36
|
+
matrix:
|
|
37
|
+
target: [aarch64-apple-darwin, x86_64-apple-darwin]
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
- uses: actions/setup-python@v5
|
|
41
|
+
with:
|
|
42
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
43
|
+
- name: Build wheel
|
|
44
|
+
uses: PyO3/maturin-action@v1
|
|
45
|
+
with:
|
|
46
|
+
target: ${{ matrix.target }}
|
|
47
|
+
args: --release --out dist
|
|
48
|
+
working-directory: clients/python
|
|
49
|
+
- uses: actions/upload-artifact@v4
|
|
50
|
+
with:
|
|
51
|
+
name: wheels-macos-${{ matrix.target }}
|
|
52
|
+
path: clients/python/dist
|
|
53
|
+
|
|
54
|
+
# --- Linux x86_64 ---
|
|
55
|
+
linux-x86:
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
- uses: actions/setup-python@v5
|
|
60
|
+
with:
|
|
61
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
62
|
+
- name: Build wheel
|
|
63
|
+
uses: PyO3/maturin-action@v1
|
|
64
|
+
with:
|
|
65
|
+
target: x86_64-unknown-linux-gnu
|
|
66
|
+
manylinux: "2_17"
|
|
67
|
+
args: --release --out dist
|
|
68
|
+
working-directory: clients/python
|
|
69
|
+
- uses: actions/upload-artifact@v4
|
|
70
|
+
with:
|
|
71
|
+
name: wheels-linux-x86_64
|
|
72
|
+
path: clients/python/dist
|
|
73
|
+
|
|
74
|
+
# --- Linux aarch64 ---
|
|
75
|
+
linux-arm:
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v4
|
|
79
|
+
- uses: actions/setup-python@v5
|
|
80
|
+
with:
|
|
81
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
82
|
+
- name: Build wheel
|
|
83
|
+
uses: PyO3/maturin-action@v1
|
|
84
|
+
with:
|
|
85
|
+
target: aarch64-unknown-linux-gnu
|
|
86
|
+
manylinux: "2_17"
|
|
87
|
+
args: --release --out dist
|
|
88
|
+
working-directory: clients/python
|
|
89
|
+
- uses: actions/upload-artifact@v4
|
|
90
|
+
with:
|
|
91
|
+
name: wheels-linux-aarch64
|
|
92
|
+
path: clients/python/dist
|
|
93
|
+
|
|
94
|
+
# --- Windows x86_64 ---
|
|
95
|
+
windows:
|
|
96
|
+
runs-on: windows-latest
|
|
97
|
+
steps:
|
|
98
|
+
- uses: actions/checkout@v4
|
|
99
|
+
- uses: actions/setup-python@v5
|
|
100
|
+
with:
|
|
101
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
102
|
+
- name: Build wheel
|
|
103
|
+
uses: PyO3/maturin-action@v1
|
|
104
|
+
with:
|
|
105
|
+
target: x86_64-pc-windows-msvc
|
|
106
|
+
args: --release --out dist
|
|
107
|
+
working-directory: clients/python
|
|
108
|
+
- uses: actions/upload-artifact@v4
|
|
109
|
+
with:
|
|
110
|
+
name: wheels-windows-x86_64
|
|
111
|
+
path: clients/python/dist
|
|
112
|
+
|
|
113
|
+
# --- Publish to PyPI ---
|
|
114
|
+
publish:
|
|
115
|
+
needs: [sdist, macos, linux-x86, linux-arm, windows]
|
|
116
|
+
runs-on: ubuntu-latest
|
|
117
|
+
environment: release
|
|
118
|
+
permissions:
|
|
119
|
+
id-token: write
|
|
120
|
+
steps:
|
|
121
|
+
- uses: actions/download-artifact@v4
|
|
122
|
+
with:
|
|
123
|
+
pattern: wheels-*
|
|
124
|
+
path: dist
|
|
125
|
+
merge-multiple: true
|
|
126
|
+
- name: Publish to PyPI
|
|
127
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
128
|
+
with:
|
|
129
|
+
packages-dir: dist/
|
llmshim-0.1.0/.gitignore
ADDED
llmshim-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What is llmshim
|
|
6
|
+
|
|
7
|
+
A pure Rust LLM API translation layer. Takes OpenAI-format JSON requests, translates them to provider-native formats (and back), with zero infrastructure requirements. Supports OpenAI (Responses API), Anthropic, Google Gemini, and xAI. Includes an interactive CLI chat with streaming, reasoning, and mid-conversation model switching.
|
|
8
|
+
|
|
9
|
+
**Published on crates.io as `llmshim`** — https://crates.io/crates/llmshim
|
|
10
|
+
|
|
11
|
+
This is a public crate. Do NOT make breaking changes to `pub` items in `src/lib.rs`, `src/router.rs`, `src/provider.rs`, `src/error.rs`, `src/fallback.rs`, `src/log.rs`, `src/config.rs`, `src/models.rs`, or `src/vision.rs` without a semver bump. The `ragents` crate (https://github.com/sanjay920/ragents) depends on this.
|
|
12
|
+
|
|
13
|
+
## Supported models
|
|
14
|
+
|
|
15
|
+
- **OpenAI:** `gpt-5.4`
|
|
16
|
+
- **Anthropic:** `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-haiku-4-5-20251001`
|
|
17
|
+
- **Gemini:** `gemini-3.1-pro-preview`, `gemini-3-flash-preview`, `gemini-3.1-flash-lite-preview`
|
|
18
|
+
- **xAI:** `grok-4-1-fast-reasoning`, `grok-4-1-fast-non-reasoning`
|
|
19
|
+
|
|
20
|
+
## Build & Test
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cargo build # dev build
|
|
24
|
+
cargo build --release # release build (~3.7MB binary)
|
|
25
|
+
cargo test --tests # unit tests (~288)
|
|
26
|
+
cargo test -- --ignored # integration tests (needs API keys)
|
|
27
|
+
cargo test --features proxy --tests # unit tests including proxy
|
|
28
|
+
cargo test --features proxy -- --ignored # all integration tests including proxy
|
|
29
|
+
cargo run # interactive CLI chat
|
|
30
|
+
cargo run --features proxy -- proxy # proxy server on :3000
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
API keys: `~/.llmshim/config.toml` (via `llmshim configure`) or env vars `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `XAI_API_KEY`. Precedence: env vars > config file.
|
|
34
|
+
|
|
35
|
+
## Architecture
|
|
36
|
+
|
|
37
|
+
### Value-based transforms, no canonical struct
|
|
38
|
+
|
|
39
|
+
Requests flow as `serde_json::Value`. Each provider's transform takes raw JSON and maps only what it understands. Provider-specific features use `x-anthropic`, `x-gemini` namespaces.
|
|
40
|
+
|
|
41
|
+
### Request flow
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
llmshim::completion(router, request)
|
|
45
|
+
→ router.resolve("anthropic/claude-sonnet-4-6") // parse "provider/model"
|
|
46
|
+
→ provider.transform_request(model, &value) // OpenAI JSON → provider-native
|
|
47
|
+
→ client.send(provider_request) // HTTP
|
|
48
|
+
→ provider.transform_response(model, body) // provider-native → OpenAI JSON
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Provider trait (`src/provider.rs`)
|
|
52
|
+
|
|
53
|
+
Every provider implements: `transform_request`, `transform_response`, `transform_stream_chunk`.
|
|
54
|
+
|
|
55
|
+
### Router (`src/router.rs`)
|
|
56
|
+
|
|
57
|
+
Parses `"provider/model"` strings. Auto-infers provider from prefix (`gpt*`/`o*` → openai, `claude*` → anthropic, `gemini*` → gemini, `grok*` → xai). Supports aliases. `Router::from_env()` reads API key env vars.
|
|
58
|
+
|
|
59
|
+
### Streaming (`src/client.rs`)
|
|
60
|
+
|
|
61
|
+
`SseStream` buffers bytes, extracts `data:` lines, routes through provider's `transform_stream_chunk`. Returns `None` to skip non-content events.
|
|
62
|
+
|
|
63
|
+
### Fallback chains (`src/fallback.rs`)
|
|
64
|
+
|
|
65
|
+
`FallbackConfig` defines an ordered list of models to try. On retryable errors (429, 500, 502, 503, 529), retries with exponential backoff then falls through to the next model. `completion_with_fallback()` is the top-level API. The proxy supports this via `"fallback": ["model1", "model2"]` in the request body.
|
|
66
|
+
|
|
67
|
+
### Vision (`src/vision.rs`)
|
|
68
|
+
|
|
69
|
+
Image content blocks are translated between providers automatically. Users can send images in any format (OpenAI `image_url`, Anthropic `image`, Gemini `inline_data`) and the correct provider sees its native format. Base64 data URIs and plain URLs are both handled. Gemini falls back to a text placeholder for URL images (only supports `inline_data`).
|
|
70
|
+
|
|
71
|
+
### Multi-model conversations
|
|
72
|
+
|
|
73
|
+
Each provider sanitizes messages from other providers in `transform_request`. OpenAI's `annotations`/`refusal` stripped for Anthropic/Gemini. `reasoning_content` stripped for all. Tool calls normalized to OpenAI format in responses, translated back per-provider on input.
|
|
74
|
+
|
|
75
|
+
### CLI (`src/main.rs`)
|
|
76
|
+
|
|
77
|
+
Single binary with subcommands: `llmshim chat` (default), `llmshim proxy`, `llmshim configure`, `llmshim set/get/list`, `llmshim models`. Interactive chat with streaming, `/model` to switch, `/clear` to reset. Reasoning on by default (`reasoning_effort: "high"`). Thinking tokens shown in dim grey, answers in default color. Final summary shows timing and token counts (`↑` input, `↓` output). Optional JSONL file logging via `--log <path>` or `LLMSHIM_LOG` env var.
|
|
78
|
+
|
|
79
|
+
### Logging (`src/log.rs`)
|
|
80
|
+
|
|
81
|
+
JSONL structured logging. Each entry: timestamp, model, provider, latency_ms, input/output/reasoning token counts, status, request_id. Logged from API-reported usage (not local counting). CLI shows summary after each response; file logging is opt-in.
|
|
82
|
+
|
|
83
|
+
### Proxy server (`src/proxy/`, feature-gated behind `proxy`)
|
|
84
|
+
|
|
85
|
+
HTTP proxy with our own API spec (not OpenAI-compatible). Built on axum.
|
|
86
|
+
|
|
87
|
+
Endpoints:
|
|
88
|
+
- `POST /v1/chat` — non-streaming (or streaming if `stream: true`)
|
|
89
|
+
- `POST /v1/chat/stream` — always SSE streaming with typed events (`content`, `reasoning`, `tool_call`, `usage`, `done`, `error`)
|
|
90
|
+
- `GET /v1/models` — list available models (filtered to configured providers)
|
|
91
|
+
- `GET /health` — health check with provider list
|
|
92
|
+
|
|
93
|
+
Request format uses `config` for provider-agnostic settings and `provider_config` for raw passthrough. OpenAPI 3.1 spec at `api/openapi.yaml`.
|
|
94
|
+
|
|
95
|
+
Run: `llmshim proxy` (requires `--features proxy` at build time)
|
|
96
|
+
Config: `LLMSHIM_HOST` (default `0.0.0.0`), `LLMSHIM_PORT` (default `3000`)
|
|
97
|
+
|
|
98
|
+
## Detailed reference
|
|
99
|
+
|
|
100
|
+
Scoped rules in `.claude/rules/` load automatically when working in relevant files:
|
|
101
|
+
- `provider-api-formats.md` — exact API formats for each provider and the proxy
|
|
102
|
+
- `testing.md` — test conventions, model IDs, gotchas
|
|
103
|
+
- `adding-a-provider.md` — full checklist for adding a new provider (all files that need changes)
|
|
104
|
+
- `env-and-config.md` — .env loading, API keys, binary targets, proxy config
|
|
105
|
+
- `before-pushing.md` — required checks before every `git push`
|
|
106
|
+
- `keep-docs-current.md` — enforces doc updates when project behavior changes
|