Metadata-Version: 2.4
Name: dotpromptz-py
Version: 1.21.0
Summary: Dotpromptz is a language-neutral executable prompt template file format for Generative AI.
Project-URL: Homepage, https://github.com/my-three-kingdoms/dotpromptz
Project-URL: Issues, https://github.com/my-three-kingdoms/dotpromptz/issues
Project-URL: Repository, https://github.com/my-three-kingdoms/dotpromptz
Author: Google
License-Expression: Apache-2.0
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.12
Requires-Dist: anthropic>=0.40
Requires-Dist: google-genai>=1.0
Requires-Dist: jinja2>=3.1
Requires-Dist: jsonschema>=4.23.0
Requires-Dist: openai>=1.0
Requires-Dist: pydantic>=2.10.6
Requires-Dist: pyyaml>=6.0.2
Requires-Dist: structlog>=24.0
Description-Content-Type: text/markdown

# dotpromptz

一个 Python 库，用于解析和渲染 `.prompt` 格式的模板文件。`.prompt` 文件基于 Jinja2 模板语法，结合 YAML 前置元数据（frontmatter），提供了一种声明式的方式来定义 LLM 提示词。

> **快速上手**：[`examples/`](./examples/) 目录包含多个可直接运行的 `.prompt` 示例文件。如果你想一次了解所有可用字段和模板语法，请参考完整字段参考模板 [`all_fields.prompt.template`](./examples/all_fields.prompt.template)（主要作为参考模板，建议复制为 `.prompt` 后按需裁剪）。

## 安装

```bash
# 作为 CLI 工具安装（推荐）
uv tool install dotpromptz-py

# 或作为项目依赖安装
uv add dotpromptz-py
```

要求 Python >= 3.12。

### 升级

```bash
# CLI 工具升级
uv tool upgrade dotpromptz-py

# 项目依赖升级
uv add --upgrade dotpromptz-py
```

也可以通过 CLI 命令直接升级：

```bash
prompt --upgrade
# 或
prompt -U
```

## 运行前凭据配置

`prompt run` 需要可用的 API 凭据。通过环境变量 `API_CREDENTIALS` 加载，支持以下形式：

1. 内联 JSON 数组：`API_CREDENTIALS='[{"name":"default","adapter":"openai","api_key":"sk-..."}]'`
2. 本地文件：`API_CREDENTIALS='@/path/to/credentials.yaml'`（也支持 `.json`）
3. 远程 URL：`API_CREDENTIALS='@https://example.com/credentials.yaml'`

如果 `API_CREDENTIALS` 未设置，dotpromptz 会尝试从默认远程地址加载凭据。拉取私有地址时可设置 `API_CREDENTIALS_TOKEN`，未设置时会尝试使用 `gh auth token` 作为兜底。

## 快速开始

### 命令行使用

```bash
# 渲染并调用 LLM，输出结果到文件
prompt run my_prompt.prompt

# 若输出文件已存在，显式允许覆盖
prompt run my_prompt.prompt --force

# 仅渲染模板（不调用 LLM），将结果写入日志目录下的 YAML 文件
prompt build my_prompt.prompt

# 合并目录中的输入文件（json/yaml/txt）
prompt merge examples/data

# 通过 glob 过滤并注入文件名（FILENAME）
prompt merge examples/data --pattern "**/*.json" -W
```

### `merge` 规则说明

1. 默认只扫描目录第一层；需要递归时请显式传 `--pattern "**/*.<ext>"`。
2. 一次合并要求文件格式一致（全部 `.json`、或全部 `.yaml/.yml`、或全部 `.txt`）。
3. 输出文件位于输入目录同级：JSON/TXT 合并为 `.jsonl`，YAML 合并为 `.yaml`。


---

## `.prompt` 文件格式

一个 `.prompt` 文件由两部分组成：

1. **YAML Frontmatter** — 配置元数据（用 `---` 包裹）
2. **模板正文** — Jinja2 模板，定义发送给 LLM 的消息内容

```
---
# YAML 前置元数据
adapter: openai
config:
  model: gpt-4o
output:
  format: txt
  file_name: "result"
---
这里是模板正文，支持 Jinja2 语法。
你好，{{ name }}！
```

---

## Frontmatter 字段参考

> **严格校验说明**：frontmatter 顶层字段仅允许以下白名单：`version`、`description`、`adapter`、`config`、`input`、`output`、`tools`、`toolDefs`、`runtime`。其余字段（包括 `foo.bar` 这类带点的键）会直接报错。

### `version`

| 属性     | 值           |
| -------- | ------------ |
| 类型     | `int`      |
| 是否必需 | **是** |

dotpromptz 的主版本号（major version），对应 `dotpromptz` 包的大版本。用于前向兼容性检查：当 `.prompt` 文件的 `version` 高于当前引擎版本时，会抛出错误。  
当存在 frontmatter 时，`version` 必须显式提供。

```yaml
version: 1
```

---

### `description`

| 属性     | 值       |
| -------- | -------- |
| 类型     | `str`  |
| 默认值   | `null` |
| 是否必需 | 否       |

对该 prompt 的人类可读描述。

```yaml
description: "生成产品描述的提示词"
```

---

### `adapter`

| 属性     | 值                         |
| -------- | -------------------------- |
| 类型     | `str` 或 `object`      |
| 默认值   | `null`                   |
| 是否必需 | **`run` 命令必需** |

指定使用的 LLM 适配器。支持的值：`openai`、`anthropic`、`google`。

**简写形式：**

```yaml
adapter: openai
```

**对象形式**（支持凭据组）：

```yaml
adapter:
  name: openai
  group: my-group        # 单个凭据组
  # 或
  groups:                 # 多个凭据组（轮询）
    - group-a
    - group-b
```

| 子字段     | 类型          | 是否必需 | 说明                 |
| ---------- | ------------- | -------- | -------------------- |
| `name`   | `str`       | 是       | 适配器名称           |
| `group`  | `str`       | 否       | 单个凭据组名         |
| `groups` | `list[str]` | 否       | 凭据组列表，轮询使用 |

### `config`

| 属性     | 值       |
| -------- | -------- |
| 类型     | `dict` |
| 默认值   | `null` |
| 是否必需 | 否       |

模型的具体配置参数。不同适配器支持的参数可能不同，以下是通用参数。在 `.prompt` 文件中统一使用 **snake_case** 命名，dotpromptz 会在内部自动映射为各 API 提供商要求的字段名。

> **注意**：`config` 采用严格字段校验。未列出的字段（例如把 `model` 误写为 `models`）会报错。

```yaml
config:
  model: gpt-4o
  thinking: high
  temperature: 1
  max_tokens: 2048
  top_p: 0.9
  seed: 42
  stop: ["\n\n"]
```

| 子字段                | 类型          | 说明              |
| --------------------- | ------------- | ----------------- |
| `model`             | `str`       | 模型名称          |
| `thinking`          | `str`       | 思考强度档位，默认 `high`（可选：`low`/`medium`/`high`） |
| `temperature`       | `float`     | 采样温度          |
| `max_tokens`        | `int`       | 最大生成 token 数 |
| `top_p`             | `float`     | 核采样参数        |
| `seed`              | `int`       | 随机种子          |
| `stop`              | `list[str]` | 停止序列          |
| `frequency_penalty` | `float`     | 频率惩罚          |
| `presence_penalty`  | `float`     | 存在惩罚          |

---

### `input`

| 属性     | 值         |
| -------- | ---------- |
| 类型     | `object` |
| 默认值   | `null`   |
| 是否必需 | 否         |

定义输入数据来源和默认值。当提供多条数据时，自动进入批量处理模式。

**简写形式**（直接作为数据）：

```yaml
input:
  name: Alice
  topic: Python
```

**完整形式**（支持 `defaults` 和 `data`）：

```yaml
input:
  defaults:
    language: Chinese
  data:
    - name: Alice
      topic: Python
    - name: Bob
      topic: Rust
```

**从文件加载**（支持多个文件路径）：

```yaml
# 单个文件
input:
  files: data/users.jsonl

# 多个文件
input:
  files:
    - data/batch_1.jsonl
    - data/batch_2.json
    - data/extra.yaml
```

> **文件路径解析规则**：`files` 中的文件路径为**相对于 `.prompt` 文件所在目录**的相对路径。例如，若 `.prompt` 文件位于 `prompts/my_task.prompt`，`files: data/users.jsonl` 会解析为 `prompts/data/users.jsonl`。也支持绝对路径。

| 子字段       | 类型                                             | 说明                                                       |
| ------------ | ------------------------------------------------ | ---------------------------------------------------------- |
| `defaults` | `dict`                                         | 固定默认值，合并到每条记录（记录中的同名字段会覆盖默认值） |
| `data`     | `dict`、`list[dict]`                             | 内联数据（单条或批量）                                   |
| `files`    | `str`、`list[str]`                              | 单个文件路径或**多个文件路径列表**                     |

> **注意**：`input.data` 与 `input.files` 互斥，不能同时设置。
> 仅设置 `defaults`（不提供 `data/files`）时，`run` 会按单条记录执行，并将 `defaults` 作为输入上下文。

**支持的数据文件格式**：

| 格式                 | 说明                                   |
| -------------------- | -------------------------------------- |
| `.json`            | 单个对象或对象数组                     |
| `.yaml` / `.yml` | 单个对象或对象数组                     |
| `.jsonl`           | 每行一个 JSON 对象（始终视为批量数据） |

---

### `output`

| 属性     | 值                         |
| -------- | -------------------------- |
| 类型     | `object`                 |
| 默认值   | `null`                   |
| 是否必需 | **`run` 命令必需** |

定义输出格式和存储路径。

```yaml
output:
  format: json
  schema: |
    name: string, The user's name
    age: integer, The user's age
    tags(array): string
  output_dir: "output/{{ NAME }}"
  file_name: "profile_{{ name }}"

```

| 子字段         | 类型     | 默认值                     | 是否必需                   | 说明                                                                 |
| -------------- | -------- | -------------------------- | -------------------------- | -------------------------------------------------------------------- |
| `format`     | `str`  | —                         | **是**               | 输出格式：`json`、`yaml`、`txt`、`image`                     |
| `schema`     | `str`  | `null`                   | 否                         | 输出的 schema 定义（Picoschema 格式），仅 `json`/`yaml` 格式有效 |
| `output_dir` | `str`  | `output/{{NAME}}_{{TIME}}` | 否                         | 输出目录模板；`run` 时支持 `{{ NAME }}`、`{{ TIME }}`          |
| `file_name`  | `str`  | —                         | **`run` 命令必需** | 输出文件名模板（不含扩展名），支持自动变量和输入变量 |

> **注意**：`run` 命令中，`output_dir` 仅支持 `{{ NAME }}`、`{{ TIME }}`。`file_name` 还支持 `{{ INDEX }}`（批量索引）和输入变量（如 `{{ name }}`、`{{ lang }}`）。批量处理时如果不同记录渲染出同名文件，默认会报错并停止，避免意外覆盖；可使用 `prompt run --force` 显式允许覆盖。

```yaml
# 示例：使用自动变量和输入变量
output:
  file_name: "profile_{{ name }}"       # 输入变量
```

```yaml
output:
  file_name: "translation_{{ INDEX }}"  # 批量索引
```

```yaml
output:
  file_name: "{{ NAME }}_{{ TIME }}"    # prompt 文件名 + 时间戳
```

```yaml
output:
  file_name: "{{ NAME }}_{{ lang }}"    # 自动变量 + 输入变量
```

---

### `tools`

| 属性     | 值            |
| -------- | ------------- |
| 类型     | `list[str]` |
| 默认值   | `null`      |
| 是否必需 | 否            |

声明可供模型调用的工具名称列表。

```yaml
tools:
  - get_weather
  - search_database
```

---

### `toolDefs`

| 属性     | 值               |
| -------- | ---------------- |
| 类型     | `list[object]` |
| 默认值   | `null`         |
| 是否必需 | 否               |

内联定义工具。每个工具包含名称、描述和参数 schema。

```yaml
toolDefs:
  - name: get_weather
    description: "获取指定城市的天气"
    inputSchema:
      type: object
      properties:
        city:
          type: string
          description: "城市名称"
      required: [city]
```

---

### `runtime`

| 属性     | 值         |
| -------- | ---------- |
| 类型     | `object` |
| 默认值   | `null`   |
| 是否必需 | 否         |

执行时的运行参数，控制并发、超时和重试。

```yaml
runtime:
  max_workers: 5
  timeout: 30.0
  retry:
    max_retries: 3
    retry_delay: 1.0
```

| 子字段                | 类型      | 默认值   | 说明                                                     |
| --------------------- | --------- | -------- | -------------------------------------------------------- |
| `max_workers`       | `int`   | `5`    | 批量处理时的最大并发数                                   |
| `timeout`           | `float` | `null` | 单次请求的超时时间（秒）                                 |
| `retry`             | `object`| `null` | 重试配置；默认不重试                                     |
| `retry.max_retries` | `int`   | `3`    | 最大重试次数（仅当设置 `retry` 时生效）                  |
| `retry.retry_delay` | `float` | `1.0`  | 重试间隔基数（秒），实际延迟 = `delay × (attempt + 1)` |

> 约束：`max_workers >= 1`，`timeout > 0`（若设置），`retry.max_retries >= 0`，`retry.retry_delay >= 0`。

---

## 模板语法

模板正文使用 [Jinja2](https://jinja.palletsprojects.com/) 语法。

### 变量插值

```jinja2
你好，{{ name }}！你对 {{ topic }} 感兴趣。
```

支持嵌套属性访问：

```jinja2
用户 {{ user.name }} 的邮箱是 {{ user.email }}。
```

### 条件判断

```jinja2
{% if premium %}
你是高级用户，享有优先服务。
{% else %}
欢迎注册成为高级用户。
{% endif %}
```

### 循环

```jinja2
你的兴趣爱好：
{% for hobby in hobbies %}
- {{ hobby }}
{% endfor %}
```

### 直接属性访问

```jinja2
姓名：{{ user.name }}
年龄：{{ user.age }}
```

---

## 自动变量（UPPERCASE 变量）

dotpromptz 自动注入以下大写变量，可在模板正文和 frontmatter 的部分字段中使用。大写变量为自动注入，小写变量为用户输入：

| 变量            | 说明                                           | 可用位置                                                   | 示例值                      |
| --------------- | ---------------------------------------------- | ---------------------------------------------------------- | --------------------------- |
| `{{ NAME }}`   | 当前 `.prompt` 文件的名称（不含扩展名）      | 模板正文、frontmatter 字段、`output_dir`、`file_name` | `draw_cat`                |
| `{{ TIME }}`   | 当前时间戳                                     | 模板正文、frontmatter 字段、`output_dir`、`file_name` | `2025_01_15_14_30_00` / `20250115_143000` |
| `{{ INDEX }}`  | 批量处理中的项目索引（从 0 开始）              | `file_name`                                              | `0`、`1`、`2`           |
| `{{ SCHEMA }}` | 从 `output.schema` 生成的 JSON Schema 字符串 | 模板正文、frontmatter 字段                                 | `{"type": "object", ...}` |

> **时间格式差异**：模板正文/frontmatter 渲染里的 `{{ TIME }}` 为 `YYYY_MM_DD_HH_MM_SS`；`run` 命令解析 `output_dir`/`file_name` 时使用 `YYYYMMDD_HHMMSS`。

### `{{ SCHEMA }}` 的使用

当你定义了 `output.schema` 后，`{{ SCHEMA }}` 会自动包含对应的 JSON Schema。你可以在模板中引用它来指导模型输出结构化数据：

```
---
output:
  format: json
  schema: |
    name: string, The user's name
    age: integer
---
请按照以下 JSON Schema 输出：
{{ SCHEMA }}

为 {{ name }} 生成一份用户档案。
```

### `{{ NAME }}`、`{{ TIME }}`、`{{ INDEX }}` 在 frontmatter 中的使用

`output_dir` 和 `file_name` 字段都支持 `{{ NAME }}`、`{{ TIME }}`：

```yaml
output:
  output_dir: "results/{{ NAME }}"
  file_name: "{{ NAME }}_{{ INDEX }}"
```

`file_name` 还支持 `{{ INDEX }}` 和输入变量（如 `{{ name }}`、`{{ lang }}`），会根据每条输入记录动态解析：

```yaml
output:
  file_name: "profile_{{ name }}"  # 每条记录生成不同的文件名
```


---

## 内置模板函数与过滤器

### `{{ role("...") }}`

切换消息角色。有效角色：`system`、`user`、`model`、`tool`。

```jinja2
{{ role("system") }}
你是一个专业的翻译助手。

{{ role("user") }}
请翻译以下内容：{{ text }}
```

默认（不使用 `{{ role() }}` 时），整个模板正文作为 `user` 角色的消息。


### `{{ media(url="...", contentType="...") }}`

嵌入媒体内容（如图片）。

```jinja2
{{ role("user") }}
请描述这张图片：
{{ media(url=imageUrl, contentType="image/png") }}
```

| 参数            | 类型    | 说明                                          |
| --------------- | ------- | --------------------------------------------- |
| `url`         | `str` | 媒体资源的 URL 或 base64 数据                 |
| `contentType` | `str` | MIME 类型（如 `image/png`、`image/jpeg`） |

> `contentType` 是兼容写法，也可以使用 `content_type`。

---

### `{{ variable | tojson(indent=N) }}`

将变量序列化为 JSON 字符串。使用 Jinja2 的 `tojson` 过滤器。

```jinja2
数据如下：
{{ userData | tojson(indent=2) }}
```

| 参数       | 类型    | 默认值   | 说明            |
| ---------- | ------- | -------- | --------------- |
| 变量     | 变量    | —       | 要序列化的变量  |
| `indent` | `int` | `null` | JSON 缩进空格数 |

---

### `{% if a == b %}...{% endif %}`

条件判断：当两个值相等时渲染内容。

```jinja2
{% if user_role == "admin" %}
你拥有管理员权限。
{% else %}
你是普通用户。
{% endif %}
```

---

### `{% if a != b %}...{% endif %}`

条件判断：当两个值不相等时渲染内容。

```jinja2
{% if status != "active" %}
你的账号状态异常。
{% endif %}
```

---


## Picoschema 格式

`output.schema` 使用 Picoschema — 一种简洁的 schema 定义语法，会自动转换为标准 JSON Schema。

### 基本类型

```
name: string
age: integer
score: number
active: boolean
data: any
```

支持的标量类型：`string`、`number`、`integer`、`boolean`、`null`、`any`。

### 可选字段

使用 `?` 标记可选字段：

```
name: string
nickname?: string
```

### 字段描述

在类型后用逗号添加描述：

```
name: string, The user's display name
age: integer, User's age in years
```

### 数组类型

```
tags(array): string
scores(array): number
```

### 嵌套对象

```
address(object):
  street: string
  city: string
  zip?: string
```

### 枚举类型

```
status(enum):
  - PENDING
  - APPROVED
  - REJECTED
```

### 通配符

允许对象包含任意额外属性：

```
config(object):
  name: string
  (*): any
```

### 完整示例

```yaml
output:
  format: json
  schema: |
    name: string, The user's full name
    age: integer, Age in years
    email?: string, Email address
    hobbies(array): string
    address(object):
      city: string
      country: string
    role(enum):
      - ADMIN
      - USER
      - GUEST
```

---

## Frontmatter 中的模板渲染

Frontmatter 中支持字符串模板渲染（**`input` 部分除外**），可在 `config`、`description` 等字段中引用变量：

```yaml
---
adapter: openai
config:
  model: "{{ model_name }}"
description: "当前模型：{{ model_name }}"
output:
  format: txt
  file_name: "report"
---
```

> **注意**：仅字符串叶子节点会被渲染；列表中的字符串不会被 frontmatter 渲染器处理。`input` 部分不会进行模板渲染。`run` 命令下的 `output.output_dir`/`output.file_name` 由 CLI 的专用模板解析逻辑处理（见上方 `output` 章节说明）。

---

## 批量处理

当 `input.data`（内联数组）或 `input.files`（指向 `.jsonl/.json/.yaml` 等文件）包含多条记录时，dotpromptz 自动进入批量处理模式：

- 每条记录独立渲染模板并调用 LLM
- 使用 `runtime.max_workers` 控制并发数（默认 5）
- 每条记录生成独立的输出文件

---

## 示例

完整的可运行示例请参考 [`examples/`](./examples/) 目录：

| 示例             | 文件                                                             | 功能演示                              |
| ---------------- | ---------------------------------------------------------------- | ------------------------------------- |
| **完整字段参考** | [`all_fields.prompt.template`](./examples/all_fields.prompt.template) | 所有 frontmatter 字段 + 模板语法一览（参考模板） |
| 基础文本生成     | [`basic_text.prompt`](./examples/basic_text.prompt)               | 单条输入、文本输出、多角色消息        |
| 批量处理         | [`batch_translate.prompt`](./examples/batch_translate.prompt)     | `.jsonl` 文件输入、批量并发         |
| 结构化 JSON 输出 | [`structured_output.prompt`](./examples/structured_output.prompt) | `SCHEMA`、Picoschema、JSON 输出    |
| 多轮对话         | [`chat_with_history.prompt`](./examples/chat_with_history.prompt) | `{{ role() }}`、多角色               |
| 媒体输入         | [`describe_image.prompt`](./examples/describe_image.prompt)       | `{{ media() }}`、图片理解               |
| 聚合分析         | [`aggregate_reviews.prompt`](./examples/aggregate_reviews.prompt) | 多文件输入、结构化总结输出             |

每个示例输出到不同的子目录下，运行方式：

```bash
prompt run examples/basic_text.prompt
# ...以此类推
```

---

## 许可证

参见 [LICENSE](./LICENSE) 文件。
