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.
Files changed (57) hide show
  1. llmshim-0.1.0/.github/workflows/publish-pypi.yml +129 -0
  2. llmshim-0.1.0/.gitignore +2 -0
  3. llmshim-0.1.0/CLAUDE.md +106 -0
  4. llmshim-0.1.0/Cargo.lock +1968 -0
  5. llmshim-0.1.0/Cargo.toml +52 -0
  6. llmshim-0.1.0/LICENSE +21 -0
  7. llmshim-0.1.0/PKG-INFO +152 -0
  8. llmshim-0.1.0/README.md +264 -0
  9. llmshim-0.1.0/examples/chat.rs +25 -0
  10. llmshim-0.1.0/examples/stream.rs +45 -0
  11. llmshim-0.1.0/llmshim/__init__.py +17 -0
  12. llmshim-0.1.0/llmshim/_client.py +246 -0
  13. llmshim-0.1.0/llmshim/_server.py +161 -0
  14. llmshim-0.1.0/pyproject.toml +34 -0
  15. llmshim-0.1.0/src/client.rs +172 -0
  16. llmshim-0.1.0/src/config.rs +106 -0
  17. llmshim-0.1.0/src/env.rs +13 -0
  18. llmshim-0.1.0/src/error.rs +27 -0
  19. llmshim-0.1.0/src/fallback.rs +177 -0
  20. llmshim-0.1.0/src/lib.rs +85 -0
  21. llmshim-0.1.0/src/log.rs +138 -0
  22. llmshim-0.1.0/src/main.rs +1251 -0
  23. llmshim-0.1.0/src/models.rs +72 -0
  24. llmshim-0.1.0/src/provider.rs +25 -0
  25. llmshim-0.1.0/src/providers/anthropic.rs +616 -0
  26. llmshim-0.1.0/src/providers/gemini.rs +537 -0
  27. llmshim-0.1.0/src/providers/mod.rs +4 -0
  28. llmshim-0.1.0/src/providers/openai.rs +356 -0
  29. llmshim-0.1.0/src/providers/xai.rs +306 -0
  30. llmshim-0.1.0/src/proxy/convert.rs +174 -0
  31. llmshim-0.1.0/src/proxy/error.rs +60 -0
  32. llmshim-0.1.0/src/proxy/handlers.rs +151 -0
  33. llmshim-0.1.0/src/proxy/mod.rs +28 -0
  34. llmshim-0.1.0/src/proxy/types.rs +167 -0
  35. llmshim-0.1.0/src/router.rs +106 -0
  36. llmshim-0.1.0/src/vision.rs +215 -0
  37. llmshim-0.1.0/tests/integration.rs +401 -0
  38. llmshim-0.1.0/tests/integration_fallback.rs +199 -0
  39. llmshim-0.1.0/tests/integration_gemini.rs +438 -0
  40. llmshim-0.1.0/tests/integration_long_context.rs +189 -0
  41. llmshim-0.1.0/tests/integration_multimodel.rs +376 -0
  42. llmshim-0.1.0/tests/integration_proxy.rs +409 -0
  43. llmshim-0.1.0/tests/integration_thinking.rs +332 -0
  44. llmshim-0.1.0/tests/integration_vision.rs +418 -0
  45. llmshim-0.1.0/tests/unit_anthropic.rs +1184 -0
  46. llmshim-0.1.0/tests/unit_fallback.rs +124 -0
  47. llmshim-0.1.0/tests/unit_gemini.rs +646 -0
  48. llmshim-0.1.0/tests/unit_log.rs +103 -0
  49. llmshim-0.1.0/tests/unit_models.rs +64 -0
  50. llmshim-0.1.0/tests/unit_multimodel.rs +295 -0
  51. llmshim-0.1.0/tests/unit_openai.rs +491 -0
  52. llmshim-0.1.0/tests/unit_proxy.rs +281 -0
  53. llmshim-0.1.0/tests/unit_proxy_convert.rs +195 -0
  54. llmshim-0.1.0/tests/unit_router.rs +258 -0
  55. llmshim-0.1.0/tests/unit_sse.rs +130 -0
  56. llmshim-0.1.0/tests/unit_vision.rs +370 -0
  57. 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/
@@ -0,0 +1,2 @@
1
+ /target
2
+ .env
@@ -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