structured-llm 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.
- structured_llm-0.1.0/.gitignore +18 -0
- structured_llm-0.1.0/.python-version +1 -0
- structured_llm-0.1.0/Makefile +16 -0
- structured_llm-0.1.0/PKG-INFO +100 -0
- structured_llm-0.1.0/README.md +88 -0
- structured_llm-0.1.0/examples/receipt_extraction.py +31 -0
- structured_llm-0.1.0/pyproject.toml +39 -0
- structured_llm-0.1.0/structured_llm/__init__.py +22 -0
- structured_llm-0.1.0/structured_llm/client.py +459 -0
- structured_llm-0.1.0/structured_llm/errors.py +54 -0
- structured_llm-0.1.0/structured_llm/parser.py +143 -0
- structured_llm-0.1.0/structured_llm/schema.py +153 -0
- structured_llm-0.1.0/tests/test_client.py +157 -0
- structured_llm-0.1.0/tests/test_parser.py +50 -0
- structured_llm-0.1.0/tests/test_schema.py +36 -0
- structured_llm-0.1.0/uv.lock +809 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.PHONY: setup lint test check
|
|
2
|
+
VENV_BIN ?= .venv/bin
|
|
3
|
+
|
|
4
|
+
setup:
|
|
5
|
+
@command -v uv >/dev/null 2>&1 || pip install uv --break-system-packages
|
|
6
|
+
@[ -d .venv ] || uv venv .venv --python 3.13
|
|
7
|
+
. .venv/bin/activate && uv sync
|
|
8
|
+
|
|
9
|
+
lint:
|
|
10
|
+
$(VENV_BIN)/ruff check --fix
|
|
11
|
+
$(VENV_BIN)/mypy .
|
|
12
|
+
|
|
13
|
+
test:
|
|
14
|
+
$(VENV_BIN)/python -m pytest . -q
|
|
15
|
+
|
|
16
|
+
check: lint test
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: structured-llm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight structured output runtime for Python LLM calls.
|
|
5
|
+
Author: structured-llm contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: openai>=1.0.0
|
|
9
|
+
Requires-Dist: pydantic>=2.0.0
|
|
10
|
+
Requires-Dist: python-dotenv>=1.2.2
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# structured-llm
|
|
14
|
+
|
|
15
|
+
一个轻量的 Python 结构化输出运行时。
|
|
16
|
+
|
|
17
|
+
它直接使用 Pydantic 类型作为 schema,不需要 `.baml` 文件、CLI、代码生成,也不需要额外的运行时编译器。默认行为是 BAML 风格的「output format prompt + 本地 JSON 提取/轻量修复 + Pydantic 校验」,因此不依赖特定供应商是否支持 `response_format`。
|
|
18
|
+
|
|
19
|
+
## 安装依赖
|
|
20
|
+
|
|
21
|
+
普通运行依赖:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uv sync --no-config --default-index https://pypi.org/simple
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
开发依赖安装在 `dev` dependency group 中,包括 `pytest`、`pytest-asyncio`、`ruff`、`mypy`:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv sync --group dev --no-config --default-index https://pypi.org/simple
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
如果需要新增开发工具依赖:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
uv add <package> --group dev --no-config --default-index https://pypi.org/simple
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 使用示例
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from pydantic import BaseModel, Field
|
|
43
|
+
from structured_llm import StructuredClient
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Receipt(BaseModel):
|
|
47
|
+
merchant: str = Field(description="商户或店铺名称")
|
|
48
|
+
total: float = Field(description="收据最终支付总金额")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
client = StructuredClient(model="gpt-4o-mini", debug=True)
|
|
52
|
+
receipt = client.run("Extract the receipt: Coffee $4.50", Receipt)
|
|
53
|
+
|
|
54
|
+
print(receipt.merchant)
|
|
55
|
+
print(receipt.total)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
默认 OpenAI-compatible provider 会从 `OPENAI_API_KEY` 和 `OPENAI_BASE_URL` 读取配置;如果代码里显式传入 `api_key` 或 `base_url`,会优先使用显式参数。`examples/receipt_extraction.py` 会通过 `python-dotenv` 自动加载本地 `.env`。
|
|
59
|
+
|
|
60
|
+
`Field(description=...)` 会渲染到默认 output format prompt。`debug=True` 会把传给 OpenAI-compatible SDK 的 request payload 和模型解析前的原始输出打印到 stderr。默认不会发送 `response_format`;只有显式设置 `mode="native"` 或 `mode="auto"` 时才会尝试 provider-native structured output。
|
|
61
|
+
|
|
62
|
+
只解析已有的 LLM 文本输出:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
raw = """
|
|
66
|
+
```json
|
|
67
|
+
{"merchant": "Coffee Shop", "total": 4.5}
|
|
68
|
+
```
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
receipt = client.parse(raw, Receipt)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 开发命令
|
|
75
|
+
|
|
76
|
+
运行测试:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
PYTHONDONTWRITEBYTECODE=1 uv run --no-config --default-index https://pypi.org/simple --group dev python -m pytest -p no:cacheprovider
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
运行 Ruff:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv run --no-config --default-index https://pypi.org/simple --group dev ruff check .
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
运行 mypy:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
uv run --no-config --default-index https://pypi.org/simple --group dev mypy structured_llm
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 当前范围
|
|
95
|
+
|
|
96
|
+
- 支持同步调用:`StructuredClient.run(...)`
|
|
97
|
+
- 支持异步调用:`StructuredClient.arun(...)`
|
|
98
|
+
- 支持本地解析:`StructuredClient.parse(...)`
|
|
99
|
+
- 支持 Pydantic `BaseModel`、`list[...]`、`dict[...]`、`Literal`、`Enum` 等 `TypeAdapter` 可处理的类型
|
|
100
|
+
- 暂不支持流式 partial object、多 provider 内置适配、BAML DSL/codegen
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# structured-llm
|
|
2
|
+
|
|
3
|
+
一个轻量的 Python 结构化输出运行时。
|
|
4
|
+
|
|
5
|
+
它直接使用 Pydantic 类型作为 schema,不需要 `.baml` 文件、CLI、代码生成,也不需要额外的运行时编译器。默认行为是 BAML 风格的「output format prompt + 本地 JSON 提取/轻量修复 + Pydantic 校验」,因此不依赖特定供应商是否支持 `response_format`。
|
|
6
|
+
|
|
7
|
+
## 安装依赖
|
|
8
|
+
|
|
9
|
+
普通运行依赖:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uv sync --no-config --default-index https://pypi.org/simple
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
开发依赖安装在 `dev` dependency group 中,包括 `pytest`、`pytest-asyncio`、`ruff`、`mypy`:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv sync --group dev --no-config --default-index https://pypi.org/simple
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
如果需要新增开发工具依赖:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uv add <package> --group dev --no-config --default-index https://pypi.org/simple
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 使用示例
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from pydantic import BaseModel, Field
|
|
31
|
+
from structured_llm import StructuredClient
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Receipt(BaseModel):
|
|
35
|
+
merchant: str = Field(description="商户或店铺名称")
|
|
36
|
+
total: float = Field(description="收据最终支付总金额")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
client = StructuredClient(model="gpt-4o-mini", debug=True)
|
|
40
|
+
receipt = client.run("Extract the receipt: Coffee $4.50", Receipt)
|
|
41
|
+
|
|
42
|
+
print(receipt.merchant)
|
|
43
|
+
print(receipt.total)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
默认 OpenAI-compatible provider 会从 `OPENAI_API_KEY` 和 `OPENAI_BASE_URL` 读取配置;如果代码里显式传入 `api_key` 或 `base_url`,会优先使用显式参数。`examples/receipt_extraction.py` 会通过 `python-dotenv` 自动加载本地 `.env`。
|
|
47
|
+
|
|
48
|
+
`Field(description=...)` 会渲染到默认 output format prompt。`debug=True` 会把传给 OpenAI-compatible SDK 的 request payload 和模型解析前的原始输出打印到 stderr。默认不会发送 `response_format`;只有显式设置 `mode="native"` 或 `mode="auto"` 时才会尝试 provider-native structured output。
|
|
49
|
+
|
|
50
|
+
只解析已有的 LLM 文本输出:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
raw = """
|
|
54
|
+
```json
|
|
55
|
+
{"merchant": "Coffee Shop", "total": 4.5}
|
|
56
|
+
```
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
receipt = client.parse(raw, Receipt)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 开发命令
|
|
63
|
+
|
|
64
|
+
运行测试:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
PYTHONDONTWRITEBYTECODE=1 uv run --no-config --default-index https://pypi.org/simple --group dev python -m pytest -p no:cacheprovider
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
运行 Ruff:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
uv run --no-config --default-index https://pypi.org/simple --group dev ruff check .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
运行 mypy:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
uv run --no-config --default-index https://pypi.org/simple --group dev mypy structured_llm
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 当前范围
|
|
83
|
+
|
|
84
|
+
- 支持同步调用:`StructuredClient.run(...)`
|
|
85
|
+
- 支持异步调用:`StructuredClient.arun(...)`
|
|
86
|
+
- 支持本地解析:`StructuredClient.parse(...)`
|
|
87
|
+
- 支持 Pydantic `BaseModel`、`list[...]`、`dict[...]`、`Literal`、`Enum` 等 `TypeAdapter` 可处理的类型
|
|
88
|
+
- 暂不支持流式 partial object、多 provider 内置适配、BAML DSL/codegen
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from dotenv import load_dotenv
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
|
|
4
|
+
from structured_llm import StructuredClient
|
|
5
|
+
|
|
6
|
+
OPENAI_MODEL = "gpt-5.4-mini"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ReceiptItem(BaseModel):
|
|
10
|
+
name: str = Field(description="购买的商品名称")
|
|
11
|
+
quantity: int = Field(description="该商品的购买数量")
|
|
12
|
+
price: float = Field(description="该商品的单价")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Receipt(BaseModel):
|
|
16
|
+
merchant: str = Field(description="商户或店铺名称")
|
|
17
|
+
items: list[ReceiptItem] = Field(description="收据中的商品明细")
|
|
18
|
+
total: float = Field(description="收据最终支付总金额")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
load_dotenv()
|
|
22
|
+
|
|
23
|
+
client = StructuredClient(model=OPENAI_MODEL, debug=True)
|
|
24
|
+
|
|
25
|
+
receipt: Receipt = client.run(
|
|
26
|
+
prompt="提取收据信息: 咖啡店, 2杯卡布奇诺, 每杯4.50, 总计9块。",
|
|
27
|
+
schema=Receipt,
|
|
28
|
+
)
|
|
29
|
+
print(receipt)
|
|
30
|
+
print("---")
|
|
31
|
+
print(receipt.model_dump_json(indent=4))
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "structured-llm"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Lightweight structured output runtime for Python LLM calls."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "structured-llm contributors" }]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"openai>=1.0.0",
|
|
15
|
+
"pydantic>=2.0.0",
|
|
16
|
+
"python-dotenv>=1.2.2",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[[tool.uv.index]]
|
|
20
|
+
url = "https://pypi.org/simple"
|
|
21
|
+
default = true
|
|
22
|
+
|
|
23
|
+
[tool.pytest.ini_options]
|
|
24
|
+
testpaths = ["tests"]
|
|
25
|
+
norecursedirs = ["sample", ".git", ".venv", ".ruff_cache"]
|
|
26
|
+
asyncio_mode = "auto"
|
|
27
|
+
|
|
28
|
+
[dependency-groups]
|
|
29
|
+
dev = [
|
|
30
|
+
"mypy>=2.1.0",
|
|
31
|
+
"pytest>=7.0.0",
|
|
32
|
+
"pytest-asyncio>=0.21.0",
|
|
33
|
+
"ruff>=0.15.20",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[tool.mypy]
|
|
37
|
+
exclude = [
|
|
38
|
+
"^sample/"
|
|
39
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from .client import StructuredClient
|
|
2
|
+
from .errors import (
|
|
3
|
+
ProviderError,
|
|
4
|
+
StructuredLLMError,
|
|
5
|
+
StructuredParseError,
|
|
6
|
+
StructuredValidationError,
|
|
7
|
+
UnsupportedSchemaError,
|
|
8
|
+
)
|
|
9
|
+
from .parser import parse_structured_text
|
|
10
|
+
from .schema import build_schema_spec
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"ProviderError",
|
|
14
|
+
"StructuredClient",
|
|
15
|
+
"StructuredLLMError",
|
|
16
|
+
"StructuredParseError",
|
|
17
|
+
"StructuredValidationError",
|
|
18
|
+
"UnsupportedSchemaError",
|
|
19
|
+
"build_schema_spec",
|
|
20
|
+
"parse_structured_text",
|
|
21
|
+
]
|
|
22
|
+
|