llmock 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 (59) hide show
  1. llmock-0.1.0/.github/CODEOWNERS +1 -0
  2. llmock-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +59 -0
  3. llmock-0.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
  4. llmock-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +39 -0
  5. llmock-0.1.0/.github/dependabot.yml +13 -0
  6. llmock-0.1.0/.github/pull_request_template.md +16 -0
  7. llmock-0.1.0/.github/workflows/ci.yml +71 -0
  8. llmock-0.1.0/.github/workflows/release.yml +87 -0
  9. llmock-0.1.0/.gitignore +45 -0
  10. llmock-0.1.0/ARCHITECTURE.md +216 -0
  11. llmock-0.1.0/CHANGELOG.md +12 -0
  12. llmock-0.1.0/CODE_OF_CONDUCT.md +27 -0
  13. llmock-0.1.0/CONTRIBUTING.md +52 -0
  14. llmock-0.1.0/LICENSE +21 -0
  15. llmock-0.1.0/PKG-INFO +378 -0
  16. llmock-0.1.0/README.md +338 -0
  17. llmock-0.1.0/RELEASING.md +125 -0
  18. llmock-0.1.0/SECURITY.md +20 -0
  19. llmock-0.1.0/examples/README.md +107 -0
  20. llmock-0.1.0/examples/chaos_scenarios.sh +107 -0
  21. llmock-0.1.0/examples/llmock.example.json +19 -0
  22. llmock-0.1.0/examples/llmock.example.yaml +15 -0
  23. llmock-0.1.0/examples/retry_with_openai.py +81 -0
  24. llmock-0.1.0/llmock/__init__.py +5 -0
  25. llmock-0.1.0/llmock/chaos.py +187 -0
  26. llmock-0.1.0/llmock/cli.py +275 -0
  27. llmock-0.1.0/llmock/errors.py +91 -0
  28. llmock-0.1.0/llmock/main.py +53 -0
  29. llmock-0.1.0/llmock/routers/__init__.py +0 -0
  30. llmock-0.1.0/llmock/routers/ai21.py +219 -0
  31. llmock-0.1.0/llmock/routers/anthropic.py +212 -0
  32. llmock-0.1.0/llmock/routers/base.py +45 -0
  33. llmock-0.1.0/llmock/routers/batch.py +678 -0
  34. llmock-0.1.0/llmock/routers/cohere.py +218 -0
  35. llmock-0.1.0/llmock/routers/gemini.py +208 -0
  36. llmock-0.1.0/llmock/routers/groq.py +254 -0
  37. llmock-0.1.0/llmock/routers/mistral.py +250 -0
  38. llmock-0.1.0/llmock/routers/openai.py +313 -0
  39. llmock-0.1.0/llmock/routers/perplexity.py +293 -0
  40. llmock-0.1.0/llmock/routers/registry.py +36 -0
  41. llmock-0.1.0/llmock/routers/together.py +245 -0
  42. llmock-0.1.0/llmock/routers/xai.py +254 -0
  43. llmock-0.1.0/llmock/simulation.py +355 -0
  44. llmock-0.1.0/pyproject.toml +70 -0
  45. llmock-0.1.0/tests/__init__.py +0 -0
  46. llmock-0.1.0/tests/conftest.py +33 -0
  47. llmock-0.1.0/tests/test_anthropic.py +69 -0
  48. llmock-0.1.0/tests/test_batch.py +605 -0
  49. llmock-0.1.0/tests/test_chaos.py +169 -0
  50. llmock-0.1.0/tests/test_cli.py +209 -0
  51. llmock-0.1.0/tests/test_error_profiles.py +102 -0
  52. llmock-0.1.0/tests/test_error_shapes.py +42 -0
  53. llmock-0.1.0/tests/test_errors.py +223 -0
  54. llmock-0.1.0/tests/test_mistral.py +50 -0
  55. llmock-0.1.0/tests/test_openai.py +73 -0
  56. llmock-0.1.0/tests/test_provider_batches.py +182 -0
  57. llmock-0.1.0/tests/test_providers.py +228 -0
  58. llmock-0.1.0/tests/test_responses.py +26 -0
  59. llmock-0.1.0/tests/test_simulation_behaviors.py +86 -0
@@ -0,0 +1 @@
1
+ * @JulienRabault
@@ -0,0 +1,59 @@
1
+ name: Bug report
2
+ description: Report a runtime, CLI, docs, or provider-shape issue
3
+ title: "[Bug] "
4
+ labels: ["bug"]
5
+ body:
6
+ - type: textarea
7
+ id: summary
8
+ attributes:
9
+ label: Summary
10
+ description: What is wrong, and what did you expect instead?
11
+ validations:
12
+ required: true
13
+
14
+ - type: dropdown
15
+ id: area
16
+ attributes:
17
+ label: Area
18
+ options:
19
+ - OpenAI-compatible routes
20
+ - Anthropic routes
21
+ - Mistral routes
22
+ - Cohere routes
23
+ - Gemini routes
24
+ - Groq routes
25
+ - Together routes
26
+ - Perplexity routes
27
+ - AI21 routes
28
+ - xAI routes
29
+ - Batch endpoints
30
+ - Chaos / config / CLI
31
+ - Packaging / release / CI
32
+ - Docs / examples
33
+ validations:
34
+ required: true
35
+
36
+ - type: textarea
37
+ id: steps
38
+ attributes:
39
+ label: Reproduction
40
+ description: Include request payloads, config, and commands when possible.
41
+ placeholder: |
42
+ 1. Start LLMock with ...
43
+ 2. Send request to ...
44
+ 3. Observe ...
45
+ validations:
46
+ required: true
47
+
48
+ - type: textarea
49
+ id: expected
50
+ attributes:
51
+ label: Expected behavior
52
+ validations:
53
+ required: true
54
+
55
+ - type: textarea
56
+ id: environment
57
+ attributes:
58
+ label: Environment
59
+ description: Python version, install method, OS, and LLMock version if relevant.
@@ -0,0 +1 @@
1
+ blank_issues_enabled: true
@@ -0,0 +1,39 @@
1
+ name: Feature request
2
+ description: Propose a scoped improvement to LLMock
3
+ title: "[Feature] "
4
+ labels: ["enhancement"]
5
+ body:
6
+ - type: textarea
7
+ id: problem
8
+ attributes:
9
+ label: Problem
10
+ description: What real user or testing problem does this solve?
11
+ validations:
12
+ required: true
13
+
14
+ - type: textarea
15
+ id: proposal
16
+ attributes:
17
+ label: Proposed solution
18
+ description: Keep it concrete and scoped.
19
+ validations:
20
+ required: true
21
+
22
+ - type: textarea
23
+ id: tradeoffs
24
+ attributes:
25
+ label: Tradeoffs
26
+ description: Maintenance cost, compatibility impact, or scope concerns.
27
+
28
+ - type: dropdown
29
+ id: fit
30
+ attributes:
31
+ label: Why it fits LLMock
32
+ options:
33
+ - Improves resilience testing
34
+ - Improves API fidelity
35
+ - Improves onboarding or adoption
36
+ - Improves packaging or maintainability
37
+ - Not sure
38
+ validations:
39
+ required: true
@@ -0,0 +1,13 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ open-pull-requests-limit: 5
8
+
9
+ - package-ecosystem: "github-actions"
10
+ directory: "/"
11
+ schedule:
12
+ interval: "weekly"
13
+ open-pull-requests-limit: 5
@@ -0,0 +1,16 @@
1
+ ## Summary
2
+
3
+ - what changed
4
+ - why it changed
5
+ - any provider or endpoint impact
6
+
7
+ ## Validation
8
+
9
+ - [ ] `python -m ruff check llmock tests examples/retry_with_openai.py`
10
+ - [ ] `pytest -q`
11
+ - [ ] docs updated if install, config, or API behavior changed
12
+
13
+ ## Risks
14
+
15
+ - behavior or compatibility risks
16
+ - release or migration impact
@@ -0,0 +1,71 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ lint:
9
+ name: Lint
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - name: Check out repository
13
+ uses: actions/checkout@v6
14
+
15
+ - name: Set up Python
16
+ uses: actions/setup-python@v6
17
+ with:
18
+ python-version: "3.11"
19
+ cache: pip
20
+
21
+ - name: Install dependencies
22
+ run: python -m pip install -e ".[dev]"
23
+
24
+ - name: Run Ruff
25
+ run: python -m ruff check llmock tests
26
+
27
+ tests:
28
+ name: Tests
29
+ runs-on: ubuntu-latest
30
+ strategy:
31
+ fail-fast: false
32
+ matrix:
33
+ python-version: ["3.11", "3.12", "3.13"]
34
+
35
+ steps:
36
+ - name: Check out repository
37
+ uses: actions/checkout@v6
38
+
39
+ - name: Set up Python
40
+ uses: actions/setup-python@v6
41
+ with:
42
+ python-version: ${{ matrix.python-version }}
43
+ cache: pip
44
+
45
+ - name: Install dependencies
46
+ run: python -m pip install -e ".[dev]"
47
+
48
+ - name: Run tests
49
+ run: pytest
50
+
51
+ package:
52
+ name: Build package
53
+ runs-on: ubuntu-latest
54
+ steps:
55
+ - name: Check out repository
56
+ uses: actions/checkout@v6
57
+
58
+ - name: Set up Python
59
+ uses: actions/setup-python@v6
60
+ with:
61
+ python-version: "3.11"
62
+ cache: pip
63
+
64
+ - name: Install build tooling
65
+ run: python -m pip install -e ".[dev]"
66
+
67
+ - name: Build distributions
68
+ run: python -m build
69
+
70
+ - name: Check package metadata
71
+ run: python -m twine check dist/*
@@ -0,0 +1,87 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ concurrency:
9
+ group: release-${{ github.ref }}
10
+ cancel-in-progress: false
11
+
12
+ jobs:
13
+ build:
14
+ name: Build and verify release
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - name: Check out repository
19
+ uses: actions/checkout@v6
20
+
21
+ - name: Set up Python
22
+ uses: actions/setup-python@v6
23
+ with:
24
+ python-version: "3.11"
25
+ cache: pip
26
+
27
+ - name: Install release tooling
28
+ run: python -m pip install -e ".[dev]"
29
+
30
+ - name: Lint
31
+ run: python -m ruff check llmock tests examples/retry_with_openai.py
32
+
33
+ - name: Test
34
+ run: pytest -q
35
+
36
+ - name: Build distributions
37
+ run: python -m build
38
+
39
+ - name: Check package metadata
40
+ run: python -m twine check dist/*
41
+
42
+ - name: Upload distributions
43
+ uses: actions/upload-artifact@v7
44
+ with:
45
+ name: dist
46
+ path: dist/*
47
+ if-no-files-found: error
48
+
49
+ publish-pypi:
50
+ name: Publish to PyPI
51
+ needs: build
52
+ runs-on: ubuntu-latest
53
+ environment:
54
+ name: pypi
55
+ url: https://pypi.org/project/llmock/
56
+ permissions:
57
+ id-token: write
58
+
59
+ steps:
60
+ - name: Download distributions
61
+ uses: actions/download-artifact@v8
62
+ with:
63
+ name: dist
64
+ path: dist
65
+
66
+ - name: Publish package to PyPI
67
+ uses: pypa/gh-action-pypi-publish@release/v1
68
+
69
+ github-release:
70
+ name: Create GitHub release
71
+ needs: publish-pypi
72
+ runs-on: ubuntu-latest
73
+ permissions:
74
+ contents: write
75
+
76
+ steps:
77
+ - name: Download distributions
78
+ uses: actions/download-artifact@v8
79
+ with:
80
+ name: dist
81
+ path: dist
82
+
83
+ - name: Create GitHub release
84
+ uses: softprops/action-gh-release@v2
85
+ with:
86
+ generate_release_notes: true
87
+ files: dist/*
@@ -0,0 +1,45 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+
7
+ # Virtual environments
8
+ .venv/
9
+ venv/
10
+ env/
11
+
12
+ # Distribution / packaging
13
+ dist/
14
+ build/
15
+ *.egg-info/
16
+ *.egg
17
+ MANIFEST
18
+
19
+ # Testing
20
+ .pytest_cache/
21
+ .ruff_cache/
22
+ .coverage
23
+ htmlcov/
24
+ .tox/
25
+
26
+ # Environment variables
27
+ .env
28
+ .env.*
29
+ !.env.example
30
+
31
+ # IDE
32
+ .vscode/
33
+ .idea/
34
+ *.swp
35
+ *.swo
36
+
37
+ # Internal agent files
38
+ agents/
39
+
40
+ # OS
41
+ .DS_Store
42
+ Thumbs.db
43
+
44
+ # Logs
45
+ *.log
@@ -0,0 +1,216 @@
1
+ # LLMock Architecture
2
+
3
+ ## Overview
4
+
5
+ LLMock is a zero-cost local mock server that presents OpenAI-compatible (and provider-native) HTTP APIs. Developers point their LLM SDK at `http://localhost:8000` instead of a real provider endpoint, then use environment variables or runtime settings to inject latency, rate-limit errors (429), and server errors (500 / 503). This lets integration tests exercise retry logic, backoff strategies, and error-handling paths without spending tokens or requiring network access.
6
+
7
+ ---
8
+
9
+ ## Request Flow
10
+
11
+ ```
12
+ Client (SDK / curl)
13
+
14
+
15
+ ┌───────────────────────────────────────────────┐
16
+ │ FastAPI Application │
17
+ │ │
18
+ │ ┌───────────────────────────────────────┐ │
19
+ │ │ ChaosMiddleware │ │
20
+ │ │ (latency injection + error sampling) │ │
21
+ │ └─────────────────┬─────────────────────┘ │
22
+ │ │ │
23
+ │ ┌─────────────────▼─────────────────────┐ │
24
+ │ │ API Router │ │
25
+ │ │ (provider-specific prefix & schema) │ │
26
+ │ └─────────────────┬─────────────────────┘ │
27
+ │ │ │
28
+ │ ┌─────────────────▼─────────────────────┐ │
29
+ │ │ Mock Response │ │
30
+ │ │ (deterministic, schema-correct JSON) │ │
31
+ │ └───────────────────────────────────────┘ │
32
+ └───────────────────────────────────────────────┘
33
+
34
+
35
+ Client receives response (or injected error)
36
+ ```
37
+
38
+ The `/health` endpoint bypasses the middleware entirely so monitoring systems remain reliable during chaos tests.
39
+
40
+ ---
41
+
42
+ ## Chaos Middleware
43
+
44
+ **File:** `llmock/chaos.py`
45
+
46
+ ### `ChaosSettings` dataclass
47
+
48
+ ```python
49
+ @dataclass
50
+ class ChaosSettings:
51
+ latency_ms: int = 0
52
+ error_rate_429: float = 0.0
53
+ error_rate_500: float = 0.0
54
+ error_rate_503: float = 0.0
55
+ ```
56
+
57
+ Loaded from environment variables at startup via `ChaosSettings.from_env()`:
58
+
59
+ | Variable | Default | Effect |
60
+ |-----------------------|---------|---------------------------------------------|
61
+ | `LLMOCK_LATENCY_MS` | `0` | Fixed delay added before every response |
62
+ | `LLMOCK_ERROR_RATE_429` | `0.0` | Probability (0–1) of returning a 429 |
63
+ | `LLMOCK_ERROR_RATE_500` | `0.0` | Probability (0–1) of returning a 500 |
64
+ | `LLMOCK_ERROR_RATE_503` | `0.0` | Probability (0–1) of returning a 503 |
65
+
66
+ Each app instance receives a `ChaosSettings` object through `create_app(chaos=...)`. The module-level `chaos_settings` value is only used to build the default exported `app`.
67
+
68
+ ### Sampling Logic
69
+
70
+ On each request the middleware:
71
+
72
+ 1. Sleeps for `latency_ms / 1000` seconds (if > 0).
73
+ 2. Draws a single random float `r ∈ [0, 1)`.
74
+ 3. Walks error rates in priority order `429 → 503 → 500`, accumulating a cumulative probability. If `r < cumulative`, returns that error immediately.
75
+
76
+ This cumulative approach means rates are additive and predictable: setting all three to `0.33` gives roughly equal chance of each error and ~1% pass-through.
77
+
78
+ ---
79
+
80
+ ## Router Pattern
81
+
82
+ **Directory:** `llmock/routers/`
83
+
84
+ Each provider has its own Python module with a consistent structure:
85
+
86
+ ```
87
+ llmock/routers/
88
+ ├── openai.py # /v1/... (OpenAI-compatible)
89
+ ├── anthropic.py # /anthropic/v1/...
90
+ ├── mistral.py # /v1/... (Mistral-compatible)
91
+ ├── gemini.py # /v1beta/models/...
92
+ ├── cohere.py # /v2/...
93
+ ├── groq.py # /openai/v1/...
94
+ ├── together.py # /together/v1/...
95
+ ├── perplexity.py # /pplx/v1/...
96
+ ├── ai21.py # /ai21/v1/...
97
+ ├── xai.py # /xai/v1/...
98
+ └── batch.py # /v1/files, /v1/batches (Batch API)
99
+ ```
100
+
101
+ ### Module structure (example: `openai.py`)
102
+
103
+ ```python
104
+ from fastapi import APIRouter
105
+ from pydantic import BaseModel
106
+
107
+ router = APIRouter(prefix="/v1", tags=["openai"])
108
+
109
+ # 1. Request models (Pydantic)
110
+ class ChatCompletionRequest(BaseModel): ...
111
+
112
+ # 2. Response models (Pydantic)
113
+ class ChatCompletionResponse(BaseModel): ...
114
+
115
+ # 3. Endpoint handlers
116
+ @router.post("/chat/completions", response_model=ChatCompletionResponse)
117
+ def chat_completions(request: ChatCompletionRequest) -> ChatCompletionResponse:
118
+ # Build and return a deterministic mock response
119
+ ...
120
+ ```
121
+
122
+ Routers are registered in `llmock/main.py` via `app.include_router(...)`.
123
+
124
+ ---
125
+
126
+ ## Batch API Simulation
127
+
128
+ **File:** `llmock/routers/batch.py`
129
+
130
+ Simulates the full OpenAI Batch API async JSONL workflow:
131
+
132
+ | Step | Request | Description |
133
+ |------|---------|-------------|
134
+ | 1 | `POST /v1/files` | Upload a JSONL request file |
135
+ | 2 | `POST /v1/batches` | Create a batch job referencing the file |
136
+ | 3 | `GET /v1/batches/{id}` | Poll status (`validating → in_progress → completed`) |
137
+ | 4 | `GET /v1/files/{id}/content` | Download the JSONL results file |
138
+ | 5 | `POST /v1/batches/{id}/cancel` | Cancel an in-progress batch |
139
+
140
+ All state is held in in-memory dictionaries (`_files`, `_batches`). Batches complete automatically after a configurable delay (`_BATCH_DELAY`, default 3 s) via a FastAPI `BackgroundTasks` coroutine.
141
+
142
+ ---
143
+
144
+ ## Adding a New Provider
145
+
146
+ 1. **Create the router file** at `llmock/routers/myprovider.py`:
147
+
148
+ ```python
149
+ from fastapi import APIRouter
150
+ from pydantic import BaseModel
151
+
152
+ router = APIRouter(prefix="/myprovider/v1", tags=["myprovider"])
153
+
154
+ class MyRequest(BaseModel):
155
+ model: str
156
+ prompt: str
157
+
158
+ @router.post("/generate")
159
+ def generate(request: MyRequest) -> dict:
160
+ return {
161
+ "id": "mock-id",
162
+ "model": request.model,
163
+ "output": f"Mock response from {request.model}.",
164
+ }
165
+ ```
166
+
167
+ 2. **Register the router** in `llmock/main.py`:
168
+
169
+ ```python
170
+ from llmock.routers import myprovider as myprovider_router
171
+ # ...
172
+ app.include_router(myprovider_router.router)
173
+ ```
174
+
175
+ 3. **Add tests** in `tests/test_myprovider.py`:
176
+
177
+ ```python
178
+ from fastapi.testclient import TestClient
179
+ from llmock.main import create_app
180
+
181
+ client = TestClient(create_app())
182
+
183
+ def test_generate():
184
+ r = client.post("/myprovider/v1/generate", json={"model": "my-model-1", "prompt": "Hello"})
185
+ assert r.status_code == 200
186
+ assert r.json()["model"] == "my-model-1"
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Test Architecture
192
+
193
+ **Directory:** `tests/`
194
+
195
+ ```
196
+ tests/
197
+ ├── test_openai.py # OpenAI chat completions, embeddings, models
198
+ ├── test_anthropic.py # Anthropic Messages API
199
+ ├── test_mistral.py # Mistral chat completions
200
+ ├── test_providers.py # Gemini, Cohere, Groq, Together, Perplexity, AI21, xAI
201
+ ├── test_batch.py # Full Batch API workflow (upload → create → poll → results)
202
+ └── test_chaos.py # ChaosMiddleware latency and error injection
203
+ ```
204
+
205
+ ### Key patterns
206
+
207
+ - **`TestClient`** (synchronous): All tests use `fastapi.testclient.TestClient` wrapping `create_app()`. This avoids the async overhead of `httpx.AsyncClient` while remaining ASGI-compatible.
208
+ - **`create_app(chaos=...)`**: The app factory accepts an optional `ChaosSettings` argument, allowing tests to inject specific chaos configurations without touching global state or environment variables.
209
+ - **Direct state injection**: Batch API tests that need a specific pre-condition (e.g., `in_progress` status) write directly into the router's `_batches` dict rather than racing against background tasks.
210
+
211
+ ### Running tests
212
+
213
+ ```bash
214
+ pip install -e ".[dev]"
215
+ pytest
216
+ ```
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project should be documented in this file.
4
+
5
+ ## [0.1.0] - 2026-03-20
6
+
7
+ - initial public release of LLMock
8
+ - multi-provider mock API support across OpenAI, Anthropic, Mistral, Cohere, Gemini, Groq, Together AI, Perplexity, AI21, and xAI
9
+ - configurable chaos injection with latency and per-status error probabilities
10
+ - configurable success payload styles
11
+ - provider-specific batch endpoint simulation
12
+ - PyPI-oriented CLI packaging and release workflows
@@ -0,0 +1,27 @@
1
+ # Code of Conduct
2
+
3
+ This project expects respectful, direct, and constructive collaboration.
4
+
5
+ ## Expected behavior
6
+
7
+ - discuss technical points in good faith
8
+ - focus on concrete behavior, tradeoffs, and evidence
9
+ - be respectful in disagreement
10
+ - assume contributors are trying to improve the project
11
+ - keep review feedback specific and actionable
12
+
13
+ ## Unacceptable behavior
14
+
15
+ - harassment, intimidation, or personal attacks
16
+ - discriminatory or hateful language
17
+ - repeated bad-faith argumentation
18
+ - doxxing or sharing private information without consent
19
+ - derailing collaboration with hostility or abuse
20
+
21
+ ## Scope
22
+
23
+ This applies to issues, pull requests, discussions, and any other project spaces.
24
+
25
+ ## Enforcement
26
+
27
+ Maintainers may edit, remove, or reject contributions and interactions that harm the project environment.
@@ -0,0 +1,52 @@
1
+ # Contributing to LLMock
2
+
3
+ Thanks for contributing.
4
+
5
+ LLMock is intentionally narrow in scope: it is a local mock server for testing LLM client resilience, retries, fallbacks, and integration behavior. Contributions are most useful when they improve fidelity, reliability, or adoption without turning the project into a full provider emulator.
6
+
7
+ ## Good contribution targets
8
+
9
+ - fix mismatches between runtime behavior and provider docs
10
+ - add tests for provider-specific request and error shapes
11
+ - improve batch endpoint coverage
12
+ - improve docs, examples, and onboarding
13
+ - add deterministic testing capabilities
14
+ - tighten packaging, release automation, and CI
15
+
16
+ ## Changes that need extra care
17
+
18
+ - large new provider surfaces
19
+ - features that increase maintenance cost a lot
20
+ - behavior that diverges from official provider APIs without being documented
21
+ - breaking CLI, config, or response-shape changes
22
+
23
+ If a change is large or changes public behavior, open an issue first.
24
+
25
+ ## Local setup
26
+
27
+ ```bash
28
+ pip install -e ".[dev]"
29
+ ```
30
+
31
+ Run checks before opening a pull request:
32
+
33
+ ```bash
34
+ python -m ruff check llmock tests examples/retry_with_openai.py
35
+ pytest -q
36
+ python -m build
37
+ ```
38
+
39
+ ## Pull request expectations
40
+
41
+ - keep changes scoped and explain the user-facing impact clearly
42
+ - add or update tests for behavioral changes
43
+ - update [README.md](/C:/Users/julie/Documents/Code_space/LLMock/README.md) when install, config, or API behavior changes
44
+ - preserve provider-specific response and error shapes where possible
45
+ - avoid unrelated refactors in the same pull request
46
+
47
+ ## Design principles
48
+
49
+ - local-first and cheap to run
50
+ - explicit over magical
51
+ - useful for resilience testing before it is exhaustive
52
+ - predictable defaults, configurable failure modes
llmock-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Julie
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.