llm-conduit 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.
- llm_conduit-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +71 -0
- llm_conduit-0.1.0/.github/ISSUE_TEMPLATE/config.yml +9 -0
- llm_conduit-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +55 -0
- llm_conduit-0.1.0/.github/ISSUE_TEMPLATE/new_provider.yml +51 -0
- llm_conduit-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +31 -0
- llm_conduit-0.1.0/.github/workflows/ci.yml +240 -0
- llm_conduit-0.1.0/.github/workflows/codeql.yml +35 -0
- llm_conduit-0.1.0/.github/workflows/publish.yml +192 -0
- llm_conduit-0.1.0/.gitignore +54 -0
- llm_conduit-0.1.0/CHANGELOG.md +48 -0
- llm_conduit-0.1.0/CONTRIBUTING.md +104 -0
- llm_conduit-0.1.0/PKG-INFO +894 -0
- llm_conduit-0.1.0/README.md +862 -0
- llm_conduit-0.1.0/conduit_sdk/__init__.py +40 -0
- llm_conduit-0.1.0/conduit_sdk/clients/__init__.py +6 -0
- llm_conduit-0.1.0/conduit_sdk/clients/embedding.py +85 -0
- llm_conduit-0.1.0/conduit_sdk/clients/image.py +81 -0
- llm_conduit-0.1.0/conduit_sdk/clients/llm.py +147 -0
- llm_conduit-0.1.0/conduit_sdk/clients/video.py +85 -0
- llm_conduit-0.1.0/conduit_sdk/core/__init__.py +32 -0
- llm_conduit-0.1.0/conduit_sdk/core/base.py +145 -0
- llm_conduit-0.1.0/conduit_sdk/core/config.py +104 -0
- llm_conduit-0.1.0/conduit_sdk/core/exceptions.py +119 -0
- llm_conduit-0.1.0/conduit_sdk/core/middleware.py +129 -0
- llm_conduit-0.1.0/conduit_sdk/core/protocols.py +93 -0
- llm_conduit-0.1.0/conduit_sdk/models/__init__.py +33 -0
- llm_conduit-0.1.0/conduit_sdk/models/common.py +94 -0
- llm_conduit-0.1.0/conduit_sdk/models/requests.py +197 -0
- llm_conduit-0.1.0/conduit_sdk/models/responses.py +202 -0
- llm_conduit-0.1.0/conduit_sdk/providers/__init__.py +14 -0
- llm_conduit-0.1.0/conduit_sdk/providers/anthropic/__init__.py +32 -0
- llm_conduit-0.1.0/conduit_sdk/providers/anthropic/llm.py +251 -0
- llm_conduit-0.1.0/conduit_sdk/providers/openai/__init__.py +33 -0
- llm_conduit-0.1.0/conduit_sdk/providers/openai/embedding.py +82 -0
- llm_conduit-0.1.0/conduit_sdk/providers/openai/image.py +111 -0
- llm_conduit-0.1.0/conduit_sdk/providers/openai/llm.py +211 -0
- llm_conduit-0.1.0/conduit_sdk/registry/__init__.py +4 -0
- llm_conduit-0.1.0/conduit_sdk/registry/model_registry.py +189 -0
- llm_conduit-0.1.0/conduit_sdk/registry/provider_registry.py +170 -0
- llm_conduit-0.1.0/conduit_sdk/utils/__init__.py +14 -0
- llm_conduit-0.1.0/conduit_sdk/utils/cost.py +165 -0
- llm_conduit-0.1.0/conduit_sdk/utils/logging.py +104 -0
- llm_conduit-0.1.0/conduit_sdk/utils/rate_limit.py +98 -0
- llm_conduit-0.1.0/conduit_sdk/utils/retry.py +70 -0
- llm_conduit-0.1.0/docs/architecture.svg +204 -0
- llm_conduit-0.1.0/examples/01_basic_llm.py +118 -0
- llm_conduit-0.1.0/examples/02_streaming.py +97 -0
- llm_conduit-0.1.0/examples/03_image_generation.py +108 -0
- llm_conduit-0.1.0/examples/04_video_generation.py +137 -0
- llm_conduit-0.1.0/examples/05_embeddings.py +139 -0
- llm_conduit-0.1.0/examples/06_cost_tracking.py +181 -0
- llm_conduit-0.1.0/examples/07_custom_middleware.py +199 -0
- llm_conduit-0.1.0/examples/08_registry.py +222 -0
- llm_conduit-0.1.0/examples/09_multimodal_pipeline.py +267 -0
- llm_conduit-0.1.0/examples/README.md +27 -0
- llm_conduit-0.1.0/examples/providers/__init__.py +1 -0
- llm_conduit-0.1.0/examples/providers/anthropic_client.py +107 -0
- llm_conduit-0.1.0/examples/providers/openai_client.py +173 -0
- llm_conduit-0.1.0/examples/providers/replicate_client.py +71 -0
- llm_conduit-0.1.0/examples/providers/runway_client.py +109 -0
- llm_conduit-0.1.0/pyproject.toml +78 -0
- llm_conduit-0.1.0/tests/__init__.py +0 -0
- llm_conduit-0.1.0/tests/conftest.py +152 -0
- llm_conduit-0.1.0/tests/providers/__init__.py +0 -0
- llm_conduit-0.1.0/tests/providers/conftest.py +253 -0
- llm_conduit-0.1.0/tests/providers/test_anthropic_llm.py +656 -0
- llm_conduit-0.1.0/tests/providers/test_openai_embedding.py +190 -0
- llm_conduit-0.1.0/tests/providers/test_openai_image.py +210 -0
- llm_conduit-0.1.0/tests/providers/test_openai_llm.py +394 -0
- llm_conduit-0.1.0/tests/test_embedding.py +45 -0
- llm_conduit-0.1.0/tests/test_image.py +54 -0
- llm_conduit-0.1.0/tests/test_llm.py +69 -0
- llm_conduit-0.1.0/tests/test_middleware.py +249 -0
- llm_conduit-0.1.0/tests/test_protocols.py +33 -0
- llm_conduit-0.1.0/tests/test_registry.py +140 -0
- llm_conduit-0.1.0/tests/test_video.py +37 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
name: Bug Report
|
|
2
|
+
description: Report a bug or unexpected behaviour
|
|
3
|
+
labels: [bug, needs-triage]
|
|
4
|
+
assignees: []
|
|
5
|
+
|
|
6
|
+
body:
|
|
7
|
+
- type: markdown
|
|
8
|
+
attributes:
|
|
9
|
+
value: |
|
|
10
|
+
Thanks for taking the time to report a bug. Please fill out the sections below.
|
|
11
|
+
|
|
12
|
+
- type: input
|
|
13
|
+
id: version
|
|
14
|
+
attributes:
|
|
15
|
+
label: llm-conduit version
|
|
16
|
+
placeholder: "e.g. 0.1.0"
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
|
|
20
|
+
- type: input
|
|
21
|
+
id: python
|
|
22
|
+
attributes:
|
|
23
|
+
label: Python version
|
|
24
|
+
placeholder: "e.g. 3.11.8"
|
|
25
|
+
validations:
|
|
26
|
+
required: true
|
|
27
|
+
|
|
28
|
+
- type: dropdown
|
|
29
|
+
id: modality
|
|
30
|
+
attributes:
|
|
31
|
+
label: Affected modality
|
|
32
|
+
multiple: true
|
|
33
|
+
options:
|
|
34
|
+
- LLMClient
|
|
35
|
+
- ImageGenClient
|
|
36
|
+
- VideoGenClient
|
|
37
|
+
- EmbeddingClient
|
|
38
|
+
- Middleware (retry / rate-limit / cost / logging)
|
|
39
|
+
- Registry (ModelRegistry / ProviderRegistry)
|
|
40
|
+
- Other
|
|
41
|
+
validations:
|
|
42
|
+
required: true
|
|
43
|
+
|
|
44
|
+
- type: textarea
|
|
45
|
+
id: description
|
|
46
|
+
attributes:
|
|
47
|
+
label: Description
|
|
48
|
+
description: Clear description of the bug and what you expected to happen.
|
|
49
|
+
validations:
|
|
50
|
+
required: true
|
|
51
|
+
|
|
52
|
+
- type: textarea
|
|
53
|
+
id: repro
|
|
54
|
+
attributes:
|
|
55
|
+
label: Minimal reproduction
|
|
56
|
+
description: Smallest possible code snippet that reproduces the issue.
|
|
57
|
+
render: python
|
|
58
|
+
validations:
|
|
59
|
+
required: true
|
|
60
|
+
|
|
61
|
+
- type: textarea
|
|
62
|
+
id: traceback
|
|
63
|
+
attributes:
|
|
64
|
+
label: Full traceback / error output
|
|
65
|
+
render: text
|
|
66
|
+
|
|
67
|
+
- type: textarea
|
|
68
|
+
id: context
|
|
69
|
+
attributes:
|
|
70
|
+
label: Additional context
|
|
71
|
+
description: Provider name, OS, any other relevant details.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
2
|
+
|
|
3
|
+
contact_links:
|
|
4
|
+
- name: Documentation
|
|
5
|
+
url: https://github.com/your-org/llm-conduit#readme
|
|
6
|
+
about: Read the README and examples/ before opening an issue.
|
|
7
|
+
- name: Discussions
|
|
8
|
+
url: https://github.com/your-org/llm-conduit/discussions
|
|
9
|
+
about: Ask questions and share ideas in Discussions.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: Feature Request
|
|
2
|
+
description: Suggest a new feature or improvement
|
|
3
|
+
labels: [enhancement]
|
|
4
|
+
assignees: []
|
|
5
|
+
|
|
6
|
+
body:
|
|
7
|
+
- type: markdown
|
|
8
|
+
attributes:
|
|
9
|
+
value: |
|
|
10
|
+
Before opening a request, please check that no existing issue covers this.
|
|
11
|
+
|
|
12
|
+
- type: dropdown
|
|
13
|
+
id: area
|
|
14
|
+
attributes:
|
|
15
|
+
label: Area
|
|
16
|
+
options:
|
|
17
|
+
- New modality client
|
|
18
|
+
- New middleware
|
|
19
|
+
- New provider adapter (examples/)
|
|
20
|
+
- Registry / config
|
|
21
|
+
- Request / response models
|
|
22
|
+
- Documentation / examples
|
|
23
|
+
- CI / tooling
|
|
24
|
+
- Other
|
|
25
|
+
validations:
|
|
26
|
+
required: true
|
|
27
|
+
|
|
28
|
+
- type: textarea
|
|
29
|
+
id: problem
|
|
30
|
+
attributes:
|
|
31
|
+
label: Problem / motivation
|
|
32
|
+
description: What problem are you trying to solve? Why does the current SDK not cover it?
|
|
33
|
+
validations:
|
|
34
|
+
required: true
|
|
35
|
+
|
|
36
|
+
- type: textarea
|
|
37
|
+
id: proposal
|
|
38
|
+
attributes:
|
|
39
|
+
label: Proposed solution
|
|
40
|
+
description: How should this work? Include API sketches or pseudocode if helpful.
|
|
41
|
+
validations:
|
|
42
|
+
required: true
|
|
43
|
+
|
|
44
|
+
- type: textarea
|
|
45
|
+
id: alternatives
|
|
46
|
+
attributes:
|
|
47
|
+
label: Alternatives considered
|
|
48
|
+
description: What else did you consider, and why did you rule it out?
|
|
49
|
+
|
|
50
|
+
- type: checkboxes
|
|
51
|
+
id: contribution
|
|
52
|
+
attributes:
|
|
53
|
+
label: Contribution
|
|
54
|
+
options:
|
|
55
|
+
- label: I am willing to open a PR for this feature.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: New Provider Adapter
|
|
2
|
+
description: Request or track a new provider adapter in examples/providers/
|
|
3
|
+
labels: [provider, enhancement]
|
|
4
|
+
assignees: []
|
|
5
|
+
|
|
6
|
+
body:
|
|
7
|
+
- type: input
|
|
8
|
+
id: provider
|
|
9
|
+
attributes:
|
|
10
|
+
label: Provider name
|
|
11
|
+
placeholder: "e.g. Stability AI, Mistral, Kling, ElevenLabs"
|
|
12
|
+
validations:
|
|
13
|
+
required: true
|
|
14
|
+
|
|
15
|
+
- type: checkboxes
|
|
16
|
+
id: modalities
|
|
17
|
+
attributes:
|
|
18
|
+
label: Modalities to support
|
|
19
|
+
options:
|
|
20
|
+
- label: LLM (chat completion + streaming)
|
|
21
|
+
- label: Image generation
|
|
22
|
+
- label: Video generation
|
|
23
|
+
- label: Embeddings
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
|
|
27
|
+
- type: input
|
|
28
|
+
id: sdk
|
|
29
|
+
attributes:
|
|
30
|
+
label: Provider's official Python SDK (if any)
|
|
31
|
+
placeholder: "e.g. pip install stability-sdk"
|
|
32
|
+
|
|
33
|
+
- type: input
|
|
34
|
+
id: docs
|
|
35
|
+
attributes:
|
|
36
|
+
label: API documentation URL
|
|
37
|
+
validations:
|
|
38
|
+
required: true
|
|
39
|
+
|
|
40
|
+
- type: textarea
|
|
41
|
+
id: notes
|
|
42
|
+
attributes:
|
|
43
|
+
label: Notes / quirks
|
|
44
|
+
description: Anything unusual about this provider's API (auth, async jobs, rate limits, etc.)
|
|
45
|
+
|
|
46
|
+
- type: checkboxes
|
|
47
|
+
id: contribution
|
|
48
|
+
attributes:
|
|
49
|
+
label: Contribution
|
|
50
|
+
options:
|
|
51
|
+
- label: I am willing to open a PR with this adapter.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- What does this PR do? 1–3 sentences. -->
|
|
4
|
+
|
|
5
|
+
## Type of change
|
|
6
|
+
|
|
7
|
+
- [ ] Bug fix
|
|
8
|
+
- [ ] New feature (new modality, middleware, registry capability)
|
|
9
|
+
- [ ] New provider adapter (in `examples/providers/`)
|
|
10
|
+
- [ ] Documentation / examples
|
|
11
|
+
- [ ] Refactor / internal improvement
|
|
12
|
+
- [ ] CI / tooling
|
|
13
|
+
|
|
14
|
+
## Checklist
|
|
15
|
+
|
|
16
|
+
- [ ] `pytest tests/` passes locally
|
|
17
|
+
- [ ] `ruff check conduit_sdk tests examples` passes
|
|
18
|
+
- [ ] New code has corresponding tests
|
|
19
|
+
- [ ] Docstrings updated on any changed public API
|
|
20
|
+
- [ ] `CHANGELOG.md` updated (for non-trivial changes)
|
|
21
|
+
|
|
22
|
+
## Breaking changes
|
|
23
|
+
|
|
24
|
+
<!-- Does this change the public interface of BaseClient, any abstract client, or any Pydantic model?
|
|
25
|
+
If yes, describe what breaks and why it is necessary. -->
|
|
26
|
+
|
|
27
|
+
None / describe here.
|
|
28
|
+
|
|
29
|
+
## Related issues
|
|
30
|
+
|
|
31
|
+
Closes #
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, develop]
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
# ──────────────────────────────────────────────────────────────
|
|
15
|
+
# Lint & format check
|
|
16
|
+
# ──────────────────────────────────────────────────────────────
|
|
17
|
+
lint:
|
|
18
|
+
name: Lint (ruff)
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- uses: astral-sh/setup-uv@v5
|
|
24
|
+
with:
|
|
25
|
+
version: "latest"
|
|
26
|
+
|
|
27
|
+
- name: Set up Python
|
|
28
|
+
uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.11"
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: uv pip install --system ruff
|
|
34
|
+
|
|
35
|
+
- name: Run ruff (lint)
|
|
36
|
+
run: ruff check conduit_sdk tests examples
|
|
37
|
+
|
|
38
|
+
- name: Run ruff (format check)
|
|
39
|
+
run: ruff format --check conduit_sdk tests examples
|
|
40
|
+
|
|
41
|
+
# ──────────────────────────────────────────────────────────────
|
|
42
|
+
# Type checking
|
|
43
|
+
# ──────────────────────────────────────────────────────────────
|
|
44
|
+
typecheck:
|
|
45
|
+
name: Type check (mypy)
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
|
|
50
|
+
- uses: astral-sh/setup-uv@v5
|
|
51
|
+
with:
|
|
52
|
+
version: "latest"
|
|
53
|
+
|
|
54
|
+
- name: Set up Python
|
|
55
|
+
uses: actions/setup-python@v5
|
|
56
|
+
with:
|
|
57
|
+
python-version: "3.11"
|
|
58
|
+
|
|
59
|
+
- name: Install project + all extras + dev deps
|
|
60
|
+
run: uv pip install --system -e ".[dev,openai,anthropic]"
|
|
61
|
+
|
|
62
|
+
- name: Run mypy
|
|
63
|
+
run: mypy conduit_sdk
|
|
64
|
+
|
|
65
|
+
# ──────────────────────────────────────────────────────────────
|
|
66
|
+
# Tests — matrix across Python 3.10, 3.11, 3.12
|
|
67
|
+
# ──────────────────────────────────────────────────────────────
|
|
68
|
+
test:
|
|
69
|
+
name: Tests (Python ${{ matrix.python-version }})
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
strategy:
|
|
72
|
+
fail-fast: false
|
|
73
|
+
matrix:
|
|
74
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
75
|
+
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
|
|
79
|
+
- uses: astral-sh/setup-uv@v5
|
|
80
|
+
with:
|
|
81
|
+
version: "latest"
|
|
82
|
+
|
|
83
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
84
|
+
uses: actions/setup-python@v5
|
|
85
|
+
with:
|
|
86
|
+
python-version: ${{ matrix.python-version }}
|
|
87
|
+
|
|
88
|
+
- name: Install project + all extras + dev deps
|
|
89
|
+
run: uv pip install --system -e ".[dev,openai,anthropic]"
|
|
90
|
+
|
|
91
|
+
- name: Run pytest with coverage
|
|
92
|
+
run: |
|
|
93
|
+
pytest tests/ \
|
|
94
|
+
--tb=short \
|
|
95
|
+
--cov=conduit_sdk \
|
|
96
|
+
--cov-report=term-missing \
|
|
97
|
+
--cov-report=xml \
|
|
98
|
+
--cov-fail-under=80
|
|
99
|
+
|
|
100
|
+
- name: Upload coverage report
|
|
101
|
+
if: matrix.python-version == '3.11'
|
|
102
|
+
uses: actions/upload-artifact@v4
|
|
103
|
+
with:
|
|
104
|
+
name: coverage-report
|
|
105
|
+
path: coverage.xml
|
|
106
|
+
retention-days: 7
|
|
107
|
+
|
|
108
|
+
# ──────────────────────────────────────────────────────────────
|
|
109
|
+
# Run example scripts (smoke test — no API keys needed)
|
|
110
|
+
# ──────────────────────────────────────────────────────────────
|
|
111
|
+
examples:
|
|
112
|
+
name: Smoke-test examples
|
|
113
|
+
runs-on: ubuntu-latest
|
|
114
|
+
needs: test
|
|
115
|
+
|
|
116
|
+
steps:
|
|
117
|
+
- uses: actions/checkout@v4
|
|
118
|
+
|
|
119
|
+
- uses: astral-sh/setup-uv@v5
|
|
120
|
+
with:
|
|
121
|
+
version: "latest"
|
|
122
|
+
|
|
123
|
+
- name: Set up Python
|
|
124
|
+
uses: actions/setup-python@v5
|
|
125
|
+
with:
|
|
126
|
+
python-version: "3.11"
|
|
127
|
+
|
|
128
|
+
- name: Install project + all extras
|
|
129
|
+
run: uv pip install --system -e ".[dev,openai,anthropic]"
|
|
130
|
+
|
|
131
|
+
- name: Run examples
|
|
132
|
+
run: |
|
|
133
|
+
for script in \
|
|
134
|
+
examples/01_basic_llm.py \
|
|
135
|
+
examples/02_streaming.py \
|
|
136
|
+
examples/03_image_generation.py \
|
|
137
|
+
examples/04_video_generation.py \
|
|
138
|
+
examples/05_embeddings.py \
|
|
139
|
+
examples/06_cost_tracking.py \
|
|
140
|
+
examples/07_custom_middleware.py \
|
|
141
|
+
examples/08_registry.py \
|
|
142
|
+
examples/09_multimodal_pipeline.py; do
|
|
143
|
+
echo "▶ Running $script"
|
|
144
|
+
python "$script"
|
|
145
|
+
echo "✓ $script passed"
|
|
146
|
+
done
|
|
147
|
+
|
|
148
|
+
# ──────────────────────────────────────────────────────────────
|
|
149
|
+
# Integration tests — real OpenAI API calls
|
|
150
|
+
# Only runs on push to main (not on every PR) to avoid burning credits.
|
|
151
|
+
# Requires OPENAI_API_KEY to be set as a GitHub repository secret.
|
|
152
|
+
# ──────────────────────────────────────────────────────────────
|
|
153
|
+
integration-openai:
|
|
154
|
+
name: Integration tests (OpenAI)
|
|
155
|
+
runs-on: ubuntu-latest
|
|
156
|
+
needs: test
|
|
157
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
158
|
+
|
|
159
|
+
steps:
|
|
160
|
+
- uses: actions/checkout@v4
|
|
161
|
+
|
|
162
|
+
- uses: astral-sh/setup-uv@v5
|
|
163
|
+
with:
|
|
164
|
+
version: "latest"
|
|
165
|
+
|
|
166
|
+
- name: Set up Python
|
|
167
|
+
uses: actions/setup-python@v5
|
|
168
|
+
with:
|
|
169
|
+
python-version: "3.11"
|
|
170
|
+
|
|
171
|
+
- name: Install project with OpenAI extra
|
|
172
|
+
run: uv pip install --system -e ".[dev,openai]"
|
|
173
|
+
|
|
174
|
+
- name: Run OpenAI integration tests
|
|
175
|
+
env:
|
|
176
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
177
|
+
run: |
|
|
178
|
+
pytest tests/providers/test_openai_llm.py \
|
|
179
|
+
tests/providers/test_openai_embedding.py \
|
|
180
|
+
tests/providers/test_openai_image.py \
|
|
181
|
+
-m integration \
|
|
182
|
+
-v \
|
|
183
|
+
--tb=short \
|
|
184
|
+
--timeout=60
|
|
185
|
+
|
|
186
|
+
# ──────────────────────────────────────────────────────────────
|
|
187
|
+
# Integration tests — real Anthropic API calls
|
|
188
|
+
# Only runs on push to main (not on every PR) to avoid burning credits.
|
|
189
|
+
# Requires ANTHROPIC_API_KEY to be set as a GitHub repository secret.
|
|
190
|
+
# ──────────────────────────────────────────────────────────────
|
|
191
|
+
integration-anthropic:
|
|
192
|
+
name: Integration tests (Anthropic)
|
|
193
|
+
runs-on: ubuntu-latest
|
|
194
|
+
needs: test
|
|
195
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
196
|
+
|
|
197
|
+
steps:
|
|
198
|
+
- uses: actions/checkout@v4
|
|
199
|
+
|
|
200
|
+
- uses: astral-sh/setup-uv@v5
|
|
201
|
+
with:
|
|
202
|
+
version: "latest"
|
|
203
|
+
|
|
204
|
+
- name: Set up Python
|
|
205
|
+
uses: actions/setup-python@v5
|
|
206
|
+
with:
|
|
207
|
+
python-version: "3.11"
|
|
208
|
+
|
|
209
|
+
- name: Install project with Anthropic extra
|
|
210
|
+
run: uv pip install --system -e ".[dev,anthropic]"
|
|
211
|
+
|
|
212
|
+
- name: Run Anthropic integration tests
|
|
213
|
+
env:
|
|
214
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
215
|
+
run: |
|
|
216
|
+
pytest tests/providers/test_anthropic_llm.py \
|
|
217
|
+
-m integration \
|
|
218
|
+
-v \
|
|
219
|
+
--tb=short \
|
|
220
|
+
--timeout=60
|
|
221
|
+
|
|
222
|
+
# ──────────────────────────────────────────────────────────────
|
|
223
|
+
# All checks must pass before merge (used as branch protection rule)
|
|
224
|
+
# ──────────────────────────────────────────────────────────────
|
|
225
|
+
ci-pass:
|
|
226
|
+
name: CI passed
|
|
227
|
+
runs-on: ubuntu-latest
|
|
228
|
+
needs: [lint, typecheck, test, examples]
|
|
229
|
+
if: always()
|
|
230
|
+
steps:
|
|
231
|
+
- name: Check all jobs
|
|
232
|
+
run: |
|
|
233
|
+
if [[ "${{ needs.lint.result }}" != "success" || \
|
|
234
|
+
"${{ needs.typecheck.result }}" != "success" || \
|
|
235
|
+
"${{ needs.test.result }}" != "success" || \
|
|
236
|
+
"${{ needs.examples.result }}" != "success" ]]; then
|
|
237
|
+
echo "One or more CI jobs failed."
|
|
238
|
+
exit 1
|
|
239
|
+
fi
|
|
240
|
+
echo "All CI jobs passed."
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: CodeQL Security Scan
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: "30 5 * * 1" # every Monday at 05:30 UTC
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
analyze:
|
|
13
|
+
name: Analyze (Python)
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
security-events: write
|
|
17
|
+
actions: read
|
|
18
|
+
contents: read
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Initialize CodeQL
|
|
24
|
+
uses: github/codeql-action/init@v3
|
|
25
|
+
with:
|
|
26
|
+
languages: python
|
|
27
|
+
queries: security-and-quality
|
|
28
|
+
|
|
29
|
+
- name: Autobuild
|
|
30
|
+
uses: github/codeql-action/autobuild@v3
|
|
31
|
+
|
|
32
|
+
- name: Perform CodeQL Analysis
|
|
33
|
+
uses: github/codeql-action/analyze@v3
|
|
34
|
+
with:
|
|
35
|
+
category: "/language:python"
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# ──────────────────────────────────────────────────────────────
|
|
4
|
+
# Triggers
|
|
5
|
+
# Stable release : git tag v1.2.3 → PyPI + GitHub Release
|
|
6
|
+
# Pre-release : git tag v1.2.3b1 → TestPyPI + GitHub pre-release
|
|
7
|
+
# Manual : Actions tab → Run workflow (choose target)
|
|
8
|
+
# ──────────────────────────────────────────────────────────────
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
tags:
|
|
12
|
+
- "v*.*.*" # v1.0.0, v1.2.3, v0.1.0 …
|
|
13
|
+
- "v*.*.*.*" # v1.0.0.post1 etc.
|
|
14
|
+
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
inputs:
|
|
17
|
+
target:
|
|
18
|
+
description: "Publish target"
|
|
19
|
+
required: true
|
|
20
|
+
default: "testpypi"
|
|
21
|
+
type: choice
|
|
22
|
+
options: [testpypi, pypi]
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
# ──────────────────────────────────────────────────────────────
|
|
26
|
+
# Lint + test before we touch PyPI
|
|
27
|
+
# ──────────────────────────────────────────────────────────────
|
|
28
|
+
verify:
|
|
29
|
+
name: Verify before publish
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
|
|
34
|
+
- uses: astral-sh/setup-uv@v5
|
|
35
|
+
with:
|
|
36
|
+
version: "latest"
|
|
37
|
+
|
|
38
|
+
- name: Set up Python
|
|
39
|
+
uses: actions/setup-python@v5
|
|
40
|
+
with:
|
|
41
|
+
python-version: "3.11"
|
|
42
|
+
|
|
43
|
+
- name: Install project + dev deps
|
|
44
|
+
run: uv pip install --system -e ".[dev,openai,anthropic]"
|
|
45
|
+
|
|
46
|
+
- name: Lint
|
|
47
|
+
run: ruff check conduit_sdk tests
|
|
48
|
+
|
|
49
|
+
- name: Tests
|
|
50
|
+
run: pytest tests/ --tb=short -q
|
|
51
|
+
|
|
52
|
+
# ──────────────────────────────────────────────────────────────
|
|
53
|
+
# Build sdist + wheel
|
|
54
|
+
# ──────────────────────────────────────────────────────────────
|
|
55
|
+
build:
|
|
56
|
+
name: Build distribution
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
needs: verify
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
|
|
62
|
+
- uses: astral-sh/setup-uv@v5
|
|
63
|
+
with:
|
|
64
|
+
version: "latest"
|
|
65
|
+
|
|
66
|
+
- name: Set up Python
|
|
67
|
+
uses: actions/setup-python@v5
|
|
68
|
+
with:
|
|
69
|
+
python-version: "3.11"
|
|
70
|
+
|
|
71
|
+
- name: Install build tools
|
|
72
|
+
run: uv pip install --system build twine
|
|
73
|
+
|
|
74
|
+
- name: Build sdist and wheel
|
|
75
|
+
run: python -m build
|
|
76
|
+
|
|
77
|
+
- name: Check distribution with twine
|
|
78
|
+
run: twine check dist/*
|
|
79
|
+
|
|
80
|
+
- name: Upload distribution artifacts
|
|
81
|
+
uses: actions/upload-artifact@v4
|
|
82
|
+
with:
|
|
83
|
+
name: dist
|
|
84
|
+
path: dist/
|
|
85
|
+
retention-days: 7
|
|
86
|
+
|
|
87
|
+
# ──────────────────────────────────────────────────────────────
|
|
88
|
+
# Detect whether this tag is a pre-release
|
|
89
|
+
# PEP 440 pre-releases: a/b/rc suffix e.g. v1.0.0a1, v1.0.0b2, v1.0.0rc1
|
|
90
|
+
# ──────────────────────────────────────────────────────────────
|
|
91
|
+
classify-tag:
|
|
92
|
+
name: Classify tag
|
|
93
|
+
runs-on: ubuntu-latest
|
|
94
|
+
if: github.event_name == 'push'
|
|
95
|
+
outputs:
|
|
96
|
+
is_prerelease: ${{ steps.check.outputs.is_prerelease }}
|
|
97
|
+
steps:
|
|
98
|
+
- name: Check if pre-release tag
|
|
99
|
+
id: check
|
|
100
|
+
run: |
|
|
101
|
+
TAG="${{ github.ref_name }}"
|
|
102
|
+
if echo "$TAG" | grep -qE '(a|b|rc|alpha|beta|dev)[0-9]*$'; then
|
|
103
|
+
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
|
|
104
|
+
else
|
|
105
|
+
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# ──────────────────────────────────────────────────────────────
|
|
109
|
+
# Publish stable releases → PyPI
|
|
110
|
+
# ──────────────────────────────────────────────────────────────
|
|
111
|
+
publish-pypi:
|
|
112
|
+
name: Publish → PyPI
|
|
113
|
+
runs-on: ubuntu-latest
|
|
114
|
+
needs: [build, classify-tag]
|
|
115
|
+
if: |
|
|
116
|
+
(github.event_name == 'push' && needs.classify-tag.outputs.is_prerelease == 'false') ||
|
|
117
|
+
(github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'pypi')
|
|
118
|
+
environment:
|
|
119
|
+
name: pypi
|
|
120
|
+
url: https://pypi.org/p/llm-conduit
|
|
121
|
+
permissions:
|
|
122
|
+
id-token: write # OIDC trusted publishing — no API token needed
|
|
123
|
+
|
|
124
|
+
steps:
|
|
125
|
+
- name: Download distribution
|
|
126
|
+
uses: actions/download-artifact@v4
|
|
127
|
+
with:
|
|
128
|
+
name: dist
|
|
129
|
+
path: dist/
|
|
130
|
+
|
|
131
|
+
- name: Publish to PyPI
|
|
132
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
133
|
+
|
|
134
|
+
# ──────────────────────────────────────────────────────────────
|
|
135
|
+
# Publish pre-releases → TestPyPI
|
|
136
|
+
# ──────────────────────────────────────────────────────────────
|
|
137
|
+
publish-testpypi:
|
|
138
|
+
name: Publish → TestPyPI
|
|
139
|
+
runs-on: ubuntu-latest
|
|
140
|
+
needs: [build, classify-tag]
|
|
141
|
+
if: |
|
|
142
|
+
(github.event_name == 'push' && needs.classify-tag.outputs.is_prerelease == 'true') ||
|
|
143
|
+
(github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'testpypi')
|
|
144
|
+
environment:
|
|
145
|
+
name: testpypi
|
|
146
|
+
url: https://test.pypi.org/p/llm-conduit
|
|
147
|
+
permissions:
|
|
148
|
+
id-token: write
|
|
149
|
+
|
|
150
|
+
steps:
|
|
151
|
+
- name: Download distribution
|
|
152
|
+
uses: actions/download-artifact@v4
|
|
153
|
+
with:
|
|
154
|
+
name: dist
|
|
155
|
+
path: dist/
|
|
156
|
+
|
|
157
|
+
- name: Publish to TestPyPI
|
|
158
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
159
|
+
with:
|
|
160
|
+
repository-url: https://test.pypi.org/legacy/
|
|
161
|
+
|
|
162
|
+
# ──────────────────────────────────────────────────────────────
|
|
163
|
+
# Auto-create GitHub Release after successful publish
|
|
164
|
+
# ──────────────────────────────────────────────────────────────
|
|
165
|
+
github-release:
|
|
166
|
+
name: Create GitHub Release
|
|
167
|
+
runs-on: ubuntu-latest
|
|
168
|
+
needs: [publish-pypi, classify-tag]
|
|
169
|
+
if: |
|
|
170
|
+
always() &&
|
|
171
|
+
github.event_name == 'push' &&
|
|
172
|
+
(needs.publish-pypi.result == 'success' || needs.publish-testpypi.result == 'success')
|
|
173
|
+
permissions:
|
|
174
|
+
contents: write # needed to create releases
|
|
175
|
+
|
|
176
|
+
steps:
|
|
177
|
+
- uses: actions/checkout@v4
|
|
178
|
+
|
|
179
|
+
- name: Download distribution
|
|
180
|
+
uses: actions/download-artifact@v4
|
|
181
|
+
with:
|
|
182
|
+
name: dist
|
|
183
|
+
path: dist/
|
|
184
|
+
|
|
185
|
+
- name: Create GitHub Release
|
|
186
|
+
uses: softprops/action-gh-release@v2
|
|
187
|
+
with:
|
|
188
|
+
tag_name: ${{ github.ref_name }}
|
|
189
|
+
name: ${{ github.ref_name }}
|
|
190
|
+
prerelease: ${{ needs.classify-tag.outputs.is_prerelease == 'true' }}
|
|
191
|
+
generate_release_notes: true # auto-fills from PR titles & commits
|
|
192
|
+
files: dist/*
|