llmcapa 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.
- llmcapa-0.1.0/.gitignore +144 -0
- llmcapa-0.1.0/DEVELOP.ja.md +189 -0
- llmcapa-0.1.0/DEVELOP.md +187 -0
- llmcapa-0.1.0/LICENSE +201 -0
- llmcapa-0.1.0/PKG-INFO +431 -0
- llmcapa-0.1.0/README.ja.md +206 -0
- llmcapa-0.1.0/README.md +206 -0
- llmcapa-0.1.0/REQUIREMENTS.md +129 -0
- llmcapa-0.1.0/pyproject.toml +37 -0
- llmcapa-0.1.0/src/llmcapa/__init__.py +74 -0
- llmcapa-0.1.0/src/llmcapa/cli.py +124 -0
- llmcapa-0.1.0/src/llmcapa/data/__init__.py +1 -0
- llmcapa-0.1.0/src/llmcapa/data/amazon.json +110 -0
- llmcapa-0.1.0/src/llmcapa/data/anthropic.json +298 -0
- llmcapa-0.1.0/src/llmcapa/data/deepseek.json +76 -0
- llmcapa-0.1.0/src/llmcapa/data/google.json +198 -0
- llmcapa-0.1.0/src/llmcapa/data/japanese.json +142 -0
- llmcapa-0.1.0/src/llmcapa/data/meta.json +116 -0
- llmcapa-0.1.0/src/llmcapa/data/microsoft.json +125 -0
- llmcapa-0.1.0/src/llmcapa/data/mistral.json +84 -0
- llmcapa-0.1.0/src/llmcapa/data/nvidia.json +111 -0
- llmcapa-0.1.0/src/llmcapa/data/openai.json +328 -0
- llmcapa-0.1.0/src/llmcapa/data/openrouter.json +10502 -0
- llmcapa-0.1.0/src/llmcapa/data/qwen.json +68 -0
- llmcapa-0.1.0/src/llmcapa/data/xai.json +68 -0
- llmcapa-0.1.0/src/llmcapa/models.py +297 -0
- llmcapa-0.1.0/src/llmcapa/registry.py +280 -0
- llmcapa-0.1.0/tests/test_advanced.py +125 -0
- llmcapa-0.1.0/tests/test_cache.py +18 -0
- llmcapa-0.1.0/tests/test_registry.py +214 -0
llmcapa-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL support
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.flatpak-builder/
|
|
51
|
+
|
|
52
|
+
# Translations
|
|
53
|
+
*.mo
|
|
54
|
+
*.pot
|
|
55
|
+
|
|
56
|
+
# Django stuff:
|
|
57
|
+
*.log
|
|
58
|
+
local_settings.py
|
|
59
|
+
db.sqlite3
|
|
60
|
+
db.sqlite3-journal
|
|
61
|
+
|
|
62
|
+
# Flask stuff:
|
|
63
|
+
instance/
|
|
64
|
+
.webassets-cache
|
|
65
|
+
|
|
66
|
+
# Scrapy stuff:
|
|
67
|
+
.scrapy
|
|
68
|
+
|
|
69
|
+
# Sphinx documentation
|
|
70
|
+
docs/_build/
|
|
71
|
+
|
|
72
|
+
# PyBuilder
|
|
73
|
+
.pybuilder/
|
|
74
|
+
target/
|
|
75
|
+
|
|
76
|
+
# Jupyter Notebook
|
|
77
|
+
.ipynb_checkpoints
|
|
78
|
+
|
|
79
|
+
# IPython
|
|
80
|
+
profile_default/
|
|
81
|
+
ipython_config.py
|
|
82
|
+
|
|
83
|
+
# pyenv
|
|
84
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
85
|
+
# intended to run in multiple environments; otherwise, check those in:
|
|
86
|
+
# .python-version
|
|
87
|
+
|
|
88
|
+
# pipenv
|
|
89
|
+
# According to pypa/pipenv#1255, pipenv should generally not be check into control.
|
|
90
|
+
# However, if you're deploying an application, you can change this to include Pipfile.lock:
|
|
91
|
+
#Pipfile.lock
|
|
92
|
+
|
|
93
|
+
# poetry
|
|
94
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
95
|
+
# This is especially true for applications, but for libraries it is also recommended.
|
|
96
|
+
#poetry.lock
|
|
97
|
+
|
|
98
|
+
# pdm
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
100
|
+
#pdm.lock
|
|
101
|
+
|
|
102
|
+
# virtualenv
|
|
103
|
+
.venv/
|
|
104
|
+
venv/
|
|
105
|
+
ENV/
|
|
106
|
+
env/
|
|
107
|
+
./env/
|
|
108
|
+
./venv/
|
|
109
|
+
|
|
110
|
+
# Spyder project settings
|
|
111
|
+
.spyderproject
|
|
112
|
+
.spyproject
|
|
113
|
+
|
|
114
|
+
# Rope project settings
|
|
115
|
+
.ropeproject
|
|
116
|
+
|
|
117
|
+
# mkdocs documentation
|
|
118
|
+
/site
|
|
119
|
+
|
|
120
|
+
# mypy
|
|
121
|
+
.mypy_cache/
|
|
122
|
+
.dmypy.json
|
|
123
|
+
dmypy.json
|
|
124
|
+
|
|
125
|
+
# Pyre type checker
|
|
126
|
+
.pyre/
|
|
127
|
+
|
|
128
|
+
# pytype static analyzer
|
|
129
|
+
.pytype/
|
|
130
|
+
|
|
131
|
+
# Cython debug symbols
|
|
132
|
+
cython_debug/
|
|
133
|
+
|
|
134
|
+
# IDEs
|
|
135
|
+
.idea/
|
|
136
|
+
.vscode/
|
|
137
|
+
*.swp
|
|
138
|
+
*.swo
|
|
139
|
+
|
|
140
|
+
# Local env files
|
|
141
|
+
.env
|
|
142
|
+
.env.sec
|
|
143
|
+
.env.local
|
|
144
|
+
.env.*.local
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# 開発者ガイド
|
|
2
|
+
|
|
3
|
+
このドキュメントでは、`llmcapa` ライブラリの開発、拡張、およびメンテナンス方法について説明します。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## プロジェクト構成
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
llmcapa/
|
|
11
|
+
├── pyproject.toml # ビルド設定 (Hatchling / PEP 621)
|
|
12
|
+
├── LICENSE # Apache License 2.0
|
|
13
|
+
├── README.md # ユーザー向けドキュメント(英語)
|
|
14
|
+
├── README.ja.md # ユーザー向けドキュメント(日本語)
|
|
15
|
+
├── DEVELOP.md # 開発者ガイド(英語)
|
|
16
|
+
├── DEVELOP.ja.md # 開発者ガイド(日本語)
|
|
17
|
+
├── src/llmcapa/
|
|
18
|
+
│ ├── __init__.py # 公開APIのエントリーポイント
|
|
19
|
+
│ ├── models.py # 機能データクラスと機能評価
|
|
20
|
+
│ ├── registry.py # インメモリレジストリ、ロード、およびOpenRouter取得
|
|
21
|
+
│ ├── cli.py # コマンドラインインターフェース
|
|
22
|
+
│ └── data/ # 同梱されているオフライン機能データ (JSON)
|
|
23
|
+
│ ├── __init__.py
|
|
24
|
+
│ ├── openai.json
|
|
25
|
+
│ ├── anthropic.json
|
|
26
|
+
│ └── ...
|
|
27
|
+
└── tests/ # ユニットテスト (pytest)
|
|
28
|
+
├── test_registry.py
|
|
29
|
+
├── test_cache.py
|
|
30
|
+
└── test_advanced.py
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 設計思想
|
|
36
|
+
|
|
37
|
+
1. **オフラインファースト**: すべてのコア機能データは、パッケージ内にJSONファイルとして静的に同梱されています。標準的な検索時にネットワークリクエストは発生しません。
|
|
38
|
+
2. **実行時依存関係ゼロ**: ライブラリはPython標準ライブラリのみで動作する必要があります。外部パッケージ(`pytest` や `build` など)は、開発およびテスト専用です。
|
|
39
|
+
3. **不変性とパフォーマンス**: `Capability` データクラスは `frozen=True` です。機能チェック時の冗長な計算を避けるため、評価結果はメモ化(内部キャッシュ)されます。
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 新しいプロバイダーの追加
|
|
44
|
+
|
|
45
|
+
新しいモデルプロバイダー(例: `cohere`)を追加する場合:
|
|
46
|
+
|
|
47
|
+
### 1. データファイルの作成
|
|
48
|
+
`src/llmcapa/data/<provider_name>.json` に新しいJSONファイルを作成します。
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"models": [
|
|
53
|
+
{
|
|
54
|
+
"provider": "cohere",
|
|
55
|
+
"model_id": "command-r-plus",
|
|
56
|
+
"display_name": "Command R+",
|
|
57
|
+
"context_window": 128000,
|
|
58
|
+
"max_output_tokens": 4000,
|
|
59
|
+
"input_modalities": ["text"],
|
|
60
|
+
"output_modalities": ["text"],
|
|
61
|
+
"supports_chat_completion": true,
|
|
62
|
+
"supports_function_calling": true,
|
|
63
|
+
"supports_json_mode": true,
|
|
64
|
+
"supports_streaming": true,
|
|
65
|
+
"supports_vision": false,
|
|
66
|
+
"supports_reasoning": false,
|
|
67
|
+
"tokenizer_name": "cohere-command",
|
|
68
|
+
"pricing": {
|
|
69
|
+
"input_per_1m": 2.5,
|
|
70
|
+
"output_per_1m": 10.0,
|
|
71
|
+
"currency": "USD"
|
|
72
|
+
},
|
|
73
|
+
"knowledge_cutoff": "2024-01",
|
|
74
|
+
"aliases": ["cohere/command-r-plus"]
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 2. プロバイダーテストリストの更新
|
|
81
|
+
`tests/test_registry.py` を開き、`test_providers()` 内の `expected` セットに新しいプロバイダー名を追加します:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
def test_providers():
|
|
85
|
+
provs = llmcapa.providers()
|
|
86
|
+
expected = {
|
|
87
|
+
"openai", "anthropic", "google",
|
|
88
|
+
"xai", "meta", "mistral", "qwen", "deepseek", "nvidia",
|
|
89
|
+
"microsoft", "amazon", "ntt", "customer-cloud", "elyza",
|
|
90
|
+
"softbank", "nec", "fujitsu", "pfn",
|
|
91
|
+
"cohere", # ここに追加
|
|
92
|
+
}
|
|
93
|
+
assert expected <= set(provs)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 新しい機能フラグの追加
|
|
99
|
+
|
|
100
|
+
新しい機能フラグ(例: `supports_structured_outputs`)を追加する場合:
|
|
101
|
+
|
|
102
|
+
### 1. データクラスの更新
|
|
103
|
+
`src/llmcapa/models.py` を開き、`Capability` データクラスにデフォルト値を持つ新しいフィールドを追加します:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
@dataclass(frozen=True)
|
|
107
|
+
class Capability:
|
|
108
|
+
...
|
|
109
|
+
supports_structured_outputs: bool = False
|
|
110
|
+
...
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 2. 代替モデルチェッカーの更新(任意)
|
|
114
|
+
追加する機能が、あるモデルが別のモデルを代替できるか検証する際に必須となる重要な機能である場合、`src/llmcapa/models.py` 内の `can_be_replaced_by()` の `features_to_check` リリストに追加します:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
def can_be_replaced_by(self, other: "Capability", required_features: Optional[List[str]] = None) -> bool:
|
|
118
|
+
...
|
|
119
|
+
if required_features is None:
|
|
120
|
+
features_to_check = [
|
|
121
|
+
"vision", "function_calling", "json_mode", "streaming",
|
|
122
|
+
"reasoning", "chat_completion", "responses_api",
|
|
123
|
+
"reasoning_effort", "thinking_budget", "image_output",
|
|
124
|
+
"audio_output", "video_output",
|
|
125
|
+
"structured_outputs" # ここに追加
|
|
126
|
+
]
|
|
127
|
+
required_features = [f for f in features_to_check if self.supports(f)]
|
|
128
|
+
...
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 3. JSONデータファイルの更新
|
|
132
|
+
`src/llmcapa/data/` 配下の関連するモデルのJSONファイルに、新しいフィールドを追加します。
|
|
133
|
+
|
|
134
|
+
### 4. ユニットテストの追加
|
|
135
|
+
`tests/test_advanced.py` または `tests/test_registry.py` にテストケースを追加し、新しい機能フラグが正しくパース、評価、およびキャッシュされることを検証します。
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 開発ワークフロー
|
|
140
|
+
|
|
141
|
+
### テストの実行
|
|
142
|
+
テストには `pytest` を使用します。プロジェクトのルートディレクトリから以下のコマンドを実行します:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# PYTHONPATHにsrcディレクトリを追加
|
|
146
|
+
set "PYTHONPATH=src;%PYTHONPATH%"
|
|
147
|
+
python -m pytest -v
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### コードの検証
|
|
151
|
+
ビルドやコミットを行う前に、すべてのPythonファイルが正常にコンパイルできるか確認します:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
python -m py_compile src/llmcapa/*.py tests/*.py
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### パッケージのビルド
|
|
158
|
+
ソース配布物(sdist)とwheelバイナリをビルドする場合:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# ビルド依存関係がインストールされていない場合はインストール
|
|
162
|
+
pip install build hatchling
|
|
163
|
+
|
|
164
|
+
# パッケージをビルド
|
|
165
|
+
python -m build
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
ビルドされたファイルは `dist/` ディレクトリ配下に生成されます。
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## OpenRouter マッピング詳細
|
|
173
|
+
|
|
174
|
+
`fetch_openrouter()` が呼び出されると、OpenRouter APIのモデルスキーマは以下のように `Capability` データクラスにマッピングされます:
|
|
175
|
+
|
|
176
|
+
| 機能フィールド | OpenRouter API フィールド | マッピングロジック / フォールバック |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| `model_id` | `id` | 完全一致 |
|
|
179
|
+
| `display_name` | `name` | 存在しない場合は `id` にフォールバック |
|
|
180
|
+
| `context_window` | `context_length` | `int` にキャスト、デフォルトは `0` |
|
|
181
|
+
| `max_output_tokens` | `top_provider.max_completion_tokens` | `int` にキャスト、デフォルトは `0` |
|
|
182
|
+
| `input_modalities` | `architecture.input_modalities` | デフォルトは `["text"]` |
|
|
183
|
+
| `output_modalities` | `architecture.output_modalities` | デフォルトは `["text"]` |
|
|
184
|
+
| `supports_function_calling` | `supported_parameters` | `"tools"` または `"tool_choice"` が存在すれば `True` |
|
|
185
|
+
| `supports_json_mode` | `supported_parameters` | `"structured_outputs"` または `"response_format"` が存在すれば `True` |
|
|
186
|
+
| `supports_reasoning` | `supported_parameters` | `"reasoning"` または `"include_reasoning"` が存在すれば `True` |
|
|
187
|
+
| `supports_reasoning_effort` | `supported_parameters` | `"reasoning"` が存在すれば `True` |
|
|
188
|
+
| `pricing` | `pricing` | `prompt` と `completion` のレートを100万トークンあたりのレートに変換 |
|
|
189
|
+
| `aliases` | `id` | 小文字に変換された `id` がエイリアスとして追加されます |
|
llmcapa-0.1.0/DEVELOP.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Developer Guide
|
|
2
|
+
|
|
3
|
+
This document explains how to develop, extend, and maintain the `llmcapa` library.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Project Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
llmcapa/
|
|
11
|
+
├── pyproject.toml # Build configuration (Hatchling / PEP 621)
|
|
12
|
+
├── LICENSE # Apache License 2.0
|
|
13
|
+
├── README.md # User documentation
|
|
14
|
+
├── DEVELOP.md # This guide
|
|
15
|
+
├── src/llmcapa/
|
|
16
|
+
│ ├── __init__.py # Public API entry point
|
|
17
|
+
│ ├── models.py # Capability dataclass and feature evaluation
|
|
18
|
+
│ ├── registry.py # In-memory registry, loading, and OpenRouter fetching
|
|
19
|
+
│ ├── cli.py # Command-line interface
|
|
20
|
+
│ └── data/ # Bundled offline capability data (JSON)
|
|
21
|
+
│ ├── __init__.py
|
|
22
|
+
│ ├── openai.json
|
|
23
|
+
│ ├── anthropic.json
|
|
24
|
+
│ └── ...
|
|
25
|
+
└── tests/ # Unit tests (pytest)
|
|
26
|
+
├── test_registry.py
|
|
27
|
+
├── test_cache.py
|
|
28
|
+
└── test_advanced.py
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Design Philosophy
|
|
34
|
+
|
|
35
|
+
1. **Offline-First**: All core capability data is bundled statically inside the package as JSON files. No network requests are made during standard lookups.
|
|
36
|
+
2. **Zero Runtime Dependencies**: The library must run using only the Python standard library. External packages (like `pytest` or `build`) are strictly for development/testing.
|
|
37
|
+
3. **Immutability & Performance**: The `Capability` dataclass is `frozen=True`. To avoid redundant calculations during feature checks, evaluation results are cached internally using memoization.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Adding a New Provider
|
|
42
|
+
|
|
43
|
+
To add a new model provider (e.g., `cohere`):
|
|
44
|
+
|
|
45
|
+
### 1. Create a Data File
|
|
46
|
+
Create a new JSON file under `src/llmcapa/data/<provider_name>.json`.
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"models": [
|
|
51
|
+
{
|
|
52
|
+
"provider": "cohere",
|
|
53
|
+
"model_id": "command-r-plus",
|
|
54
|
+
"display_name": "Command R+",
|
|
55
|
+
"context_window": 128000,
|
|
56
|
+
"max_output_tokens": 4000,
|
|
57
|
+
"input_modalities": ["text"],
|
|
58
|
+
"output_modalities": ["text"],
|
|
59
|
+
"supports_chat_completion": true,
|
|
60
|
+
"supports_function_calling": true,
|
|
61
|
+
"supports_json_mode": true,
|
|
62
|
+
"supports_streaming": true,
|
|
63
|
+
"supports_vision": false,
|
|
64
|
+
"supports_reasoning": false,
|
|
65
|
+
"tokenizer_name": "cohere-command",
|
|
66
|
+
"pricing": {
|
|
67
|
+
"input_per_1m": 2.5,
|
|
68
|
+
"output_per_1m": 10.0,
|
|
69
|
+
"currency": "USD"
|
|
70
|
+
},
|
|
71
|
+
"knowledge_cutoff": "2024-01",
|
|
72
|
+
"aliases": ["cohere/command-r-plus"]
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 2. Update the Provider Test List
|
|
79
|
+
Open `tests/test_registry.py` and add your new provider name to the `expected` set in `test_providers()`:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
def test_providers():
|
|
83
|
+
provs = llmcapa.providers()
|
|
84
|
+
expected = {
|
|
85
|
+
"openai", "anthropic", "google",
|
|
86
|
+
"xai", "meta", "mistral", "qwen", "deepseek", "nvidia",
|
|
87
|
+
"microsoft", "amazon", "ntt", "customer-cloud", "elyza",
|
|
88
|
+
"softbank", "nec", "fujitsu", "pfn",
|
|
89
|
+
"cohere", # Add here
|
|
90
|
+
}
|
|
91
|
+
assert expected <= set(provs)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Adding a New Feature Flag
|
|
97
|
+
|
|
98
|
+
To add a new capability/feature flag (e.g., `supports_structured_outputs`):
|
|
99
|
+
|
|
100
|
+
### 1. Update the Dataclass
|
|
101
|
+
Open `src/llmcapa/models.py` and add the new field to the `Capability` dataclass with a default value:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
@dataclass(frozen=True)
|
|
105
|
+
class Capability:
|
|
106
|
+
...
|
|
107
|
+
supports_structured_outputs: bool = False
|
|
108
|
+
...
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 2. Update the Replacement Checker (Optional)
|
|
112
|
+
If the new feature is a critical capability that should be verified when checking if one model can replace another, add it to the `features_to_check` list in `can_be_replaced_by()` inside `src/llmcapa/models.py`:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
def can_be_replaced_by(self, other: "Capability", required_features: Optional[List[str]] = None) -> bool:
|
|
116
|
+
...
|
|
117
|
+
if required_features is None:
|
|
118
|
+
features_to_check = [
|
|
119
|
+
"vision", "function_calling", "json_mode", "streaming",
|
|
120
|
+
"reasoning", "chat_completion", "responses_api",
|
|
121
|
+
"reasoning_effort", "thinking_budget", "image_output",
|
|
122
|
+
"audio_output", "video_output",
|
|
123
|
+
"structured_outputs" # Add here
|
|
124
|
+
]
|
|
125
|
+
required_features = [f for f in features_to_check if self.supports(f)]
|
|
126
|
+
...
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 3. Update JSON Data Files
|
|
130
|
+
Add the new field to the relevant models in the JSON files under `src/llmcapa/data/`.
|
|
131
|
+
|
|
132
|
+
### 4. Add Unit Tests
|
|
133
|
+
Add test cases in `tests/test_advanced.py` or `tests/test_registry.py` to verify that the new feature flag is correctly parsed, evaluated, and cached.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Development Workflow
|
|
138
|
+
|
|
139
|
+
### Running Tests
|
|
140
|
+
We use `pytest` for testing. Run the following command from the project root directory:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Set PYTHONPATH to include the src directory
|
|
144
|
+
set "PYTHONPATH=src;%PYTHONPATH%"
|
|
145
|
+
python -m pytest -v
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Code Verification
|
|
149
|
+
Before building or committing, verify that all Python files compile successfully:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
python -m py_compile src/llmcapa/*.py tests/*.py
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Building the Package
|
|
156
|
+
To build the source distribution (sdist) and wheel binary:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Install build dependencies if not already installed
|
|
160
|
+
pip install build hatchling
|
|
161
|
+
|
|
162
|
+
# Build the package
|
|
163
|
+
python -m build
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The built files will be generated under the `dist/` directory.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## OpenRouter Mapping Details
|
|
171
|
+
|
|
172
|
+
When `fetch_openrouter()` is called, it maps the OpenRouter API model schema to our `Capability` dataclass as follows:
|
|
173
|
+
|
|
174
|
+
| Capability Field | OpenRouter API Field | Mapping Logic / Fallback |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| `model_id` | `id` | Exact match |
|
|
177
|
+
| `display_name` | `name` | Falls back to `id` if missing |
|
|
178
|
+
| `context_window` | `context_length` | Cast to `int`, default `0` |
|
|
179
|
+
| `max_output_tokens` | `top_provider.max_completion_tokens` | Cast to `int`, default `0` |
|
|
180
|
+
| `input_modalities` | `architecture.input_modalities` | Default `["text"]` |
|
|
181
|
+
| `output_modalities` | `architecture.output_modalities` | Default `["text"]` |
|
|
182
|
+
| `supports_function_calling` | `supported_parameters` | `True` if `"tools"` or `"tool_choice"` is present |
|
|
183
|
+
| `supports_json_mode` | `supported_parameters` | `True` if `"structured_outputs"` or `"response_format"` is present |
|
|
184
|
+
| `supports_reasoning` | `supported_parameters` | `True` if `"reasoning"` or `"include_reasoning"` is present |
|
|
185
|
+
| `supports_reasoning_effort` | `supported_parameters` | `True` if `"reasoning"` is present |
|
|
186
|
+
| `pricing` | `pricing` | Converts `prompt` and `completion` rates to per-1M token rates |
|
|
187
|
+
| `aliases` | `id` | Lowercased `id` is added as an alias |
|