promptmap 0.5.12__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.
- promptmap-0.5.12/PKG-INFO +101 -0
- promptmap-0.5.12/README.md +85 -0
- promptmap-0.5.12/promptmap/__init__.py +479 -0
- promptmap-0.5.12/promptmap/classifier.py +165 -0
- promptmap-0.5.12/promptmap/cli/__init__.py +828 -0
- promptmap-0.5.12/promptmap/cli/init_files.py +694 -0
- promptmap-0.5.12/promptmap/cli/pretty_print.py +273 -0
- promptmap-0.5.12/promptmap/cli/test.py +560 -0
- promptmap-0.5.12/promptmap/cli/type_hints.py +249 -0
- promptmap-0.5.12/promptmap/cli/vscode.py +33 -0
- promptmap-0.5.12/promptmap/db.py +124 -0
- promptmap-0.5.12/promptmap/docs.py +282 -0
- promptmap-0.5.12/promptmap/executor/__init__.py +48 -0
- promptmap-0.5.12/promptmap/executor/builtin_kinds.py +467 -0
- promptmap-0.5.12/promptmap/executor/executor.py +524 -0
- promptmap-0.5.12/promptmap/executor/graph.py +251 -0
- promptmap-0.5.12/promptmap/executor/parser_initial.py +191 -0
- promptmap-0.5.12/promptmap/executor/parser_model.py +223 -0
- promptmap-0.5.12/promptmap/executor/run_context.py +580 -0
- promptmap-0.5.12/promptmap/expectations.py +42 -0
- promptmap-0.5.12/promptmap/files.py +910 -0
- promptmap-0.5.12/promptmap/gates.py +158 -0
- promptmap-0.5.12/promptmap/global_instruction.py +59 -0
- promptmap-0.5.12/promptmap/html_templates/_nav.html +8 -0
- promptmap-0.5.12/promptmap/html_templates/docs.html +324 -0
- promptmap-0.5.12/promptmap/html_templates/run.html +96 -0
- promptmap-0.5.12/promptmap/jsonpath.py +121 -0
- promptmap-0.5.12/promptmap/llm.py +161 -0
- promptmap-0.5.12/promptmap/model_spec.py +64 -0
- promptmap-0.5.12/promptmap/model_types.py +238 -0
- promptmap-0.5.12/promptmap/project_values.py +70 -0
- promptmap-0.5.12/promptmap/promptparams.py +444 -0
- promptmap-0.5.12/promptmap/rag.py +52 -0
- promptmap-0.5.12/promptmap/run_ids.py +120 -0
- promptmap-0.5.12/promptmap/secrets.py +80 -0
- promptmap-0.5.12/promptmap/server/__init__.py +28 -0
- promptmap-0.5.12/promptmap/server/__main__.py +36 -0
- promptmap-0.5.12/promptmap/server/app.py +259 -0
- promptmap-0.5.12/promptmap/storage/__init__.py +15 -0
- promptmap-0.5.12/promptmap/storage/base.py +69 -0
- promptmap-0.5.12/promptmap/storage/memory.py +255 -0
- promptmap-0.5.12/promptmap/storage/sqlite.py +457 -0
- promptmap-0.5.12/promptmap/tester.py +475 -0
- promptmap-0.5.12/promptmap/tokens.py +64 -0
- promptmap-0.5.12/promptmap/types.py +25 -0
- promptmap-0.5.12/promptmap/validator.py +113 -0
- promptmap-0.5.12/promptmap-extra-model-kinds/src/promptmap_extra_model_kinds/__init__.py +9 -0
- promptmap-0.5.12/promptmap-extra-model-kinds/src/promptmap_extra_model_kinds/agent/run_agent.py +517 -0
- promptmap-0.5.12/promptmap-extra-model-kinds/src/promptmap_extra_model_kinds/agent37_exec.py +881 -0
- promptmap-0.5.12/promptmap-extra-model-kinds/src/promptmap_extra_model_kinds/agent_exec.py +659 -0
- promptmap-0.5.12/promptmap-extra-model-kinds/src/promptmap_extra_model_kinds/modal_exec.py +422 -0
- promptmap-0.5.12/promptmap-extra-model-kinds/src/promptmap_extra_model_kinds/models.py +29 -0
- promptmap-0.5.12/promptmap.egg-info/PKG-INFO +101 -0
- promptmap-0.5.12/promptmap.egg-info/SOURCES.txt +84 -0
- promptmap-0.5.12/promptmap.egg-info/dependency_links.txt +1 -0
- promptmap-0.5.12/promptmap.egg-info/entry_points.txt +2 -0
- promptmap-0.5.12/promptmap.egg-info/requires.txt +12 -0
- promptmap-0.5.12/promptmap.egg-info/top_level.txt +2 -0
- promptmap-0.5.12/pyproject.toml +51 -0
- promptmap-0.5.12/setup.cfg +4 -0
- promptmap-0.5.12/tests/test_agent_kind.py +169 -0
- promptmap-0.5.12/tests/test_agent_opencode.py +179 -0
- promptmap-0.5.12/tests/test_cache_opt_out.py +52 -0
- promptmap-0.5.12/tests/test_classifier.py +322 -0
- promptmap-0.5.12/tests/test_cli.py +105 -0
- promptmap-0.5.12/tests/test_config_keys.py +92 -0
- promptmap-0.5.12/tests/test_each.py +226 -0
- promptmap-0.5.12/tests/test_each_fanout.py +228 -0
- promptmap-0.5.12/tests/test_expectations.py +30 -0
- promptmap-0.5.12/tests/test_files.py +618 -0
- promptmap-0.5.12/tests/test_gates.py +126 -0
- promptmap-0.5.12/tests/test_global_instruction.py +212 -0
- promptmap-0.5.12/tests/test_integration_agent.py +66 -0
- promptmap-0.5.12/tests/test_integration_opencode.py +81 -0
- promptmap-0.5.12/tests/test_main.py +77 -0
- promptmap-0.5.12/tests/test_model_folders.py +33 -0
- promptmap-0.5.12/tests/test_model_types.py +192 -0
- promptmap-0.5.12/tests/test_plugins.py +100 -0
- promptmap-0.5.12/tests/test_progress_callbacks.py +91 -0
- promptmap-0.5.12/tests/test_project_values.py +152 -0
- promptmap-0.5.12/tests/test_promptparams.py +727 -0
- promptmap-0.5.12/tests/test_python_ref_cache.py +37 -0
- promptmap-0.5.12/tests/test_run_ids.py +359 -0
- promptmap-0.5.12/tests/test_secrets.py +260 -0
- promptmap-0.5.12/tests/test_test_cache.py +54 -0
- promptmap-0.5.12/tests/test_tokens.py +142 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: promptmap
|
|
3
|
+
Version: 0.5.12
|
|
4
|
+
Summary: A dbt-inspired prompt orchestration tool for LLMs
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: click>=8.1
|
|
8
|
+
Requires-Dist: jinja2>=3.1
|
|
9
|
+
Requires-Dist: networkx>=3.0
|
|
10
|
+
Requires-Dist: rich>=13.0
|
|
11
|
+
Requires-Dist: python-dotenv>=1.2.2
|
|
12
|
+
Requires-Dist: pyyaml>=6.0
|
|
13
|
+
Requires-Dist: mini-swe-agent>=2.0
|
|
14
|
+
Provides-Extra: extras
|
|
15
|
+
Requires-Dist: promptmap-extra-model-kinds; python_version >= "3.11" and extra == "extras"
|
|
16
|
+
|
|
17
|
+
# promptmap
|
|
18
|
+
|
|
19
|
+
A **data-engineering-inspired** prompt orchestration tool for LLMs (think `dbt` for prompts).
|
|
20
|
+
|
|
21
|
+
**Why `promptmap`?** Treating complex LLM workflows as giant one-shot prompts is like navigating a city by staring at your feet, one wrong turn and you're lost. `promptmap` turns your prompts into a visual map of connected hypotheses (`models`) and reality-check landmarks (`tests`), with built-in tools to inspect runs (`promptmap docs`).
|
|
22
|
+
|
|
23
|
+
**Mechanically, this means you:** write modular prompts in Jinja2, chain them into a dependency graph with `ref()`, and validate outputs against reality with `promptmap test`. Iterate on complex LLM workflows without losing context or rewriting from scratch. *(Read more on the philosophy in [motivation.md](motivation.md)).*
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv tool install promptmap --with anthropic # or: openai | gemini
|
|
33
|
+
promptmap init --provider anthropic # or: openai | gemini
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Edit your `.prompt` files in `map/`, then run the pipeline, test expectations, and inspect the run:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
export ANTHROPIC_API_KEY=your_key_here
|
|
41
|
+
|
|
42
|
+
promptmap run # Execute the prompt DAG
|
|
43
|
+
promptmap test # Validate outputs against tests/
|
|
44
|
+
promptmap docs # Generate HTML report of the run and run history
|
|
45
|
+
promptmap serve # Launch the interactive local UI / HTTP server
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Core concepts
|
|
51
|
+
|
|
52
|
+
### 1. Models (`map/*.prompt`)
|
|
53
|
+
|
|
54
|
+
Each `.prompt` file is a node in your DAG. Use `ref('model_name')` to inject the output of an upstream model:
|
|
55
|
+
|
|
56
|
+
```jinja
|
|
57
|
+
{# map/outline.prompt #}
|
|
58
|
+
Based on this topic, create a detailed outline:
|
|
59
|
+
|
|
60
|
+
{{ ref('topic') }}
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 2. Model types (`config(model_type=...)`)
|
|
65
|
+
|
|
66
|
+
Not every node in your pipeline needs to be a standard LLM call. Override `model_type` at the top of a `.prompt` file for deterministic steps, formatting, or coding agents:
|
|
67
|
+
|
|
68
|
+
| Model Type | What it does | Declaration |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| **LLM** *(default)* | Sends the rendered Jinja prompt to your configured LLM. | Standard `.prompt` file |
|
|
71
|
+
| **`template`** | Renders Jinja directly with no LLM call (for reshaping upstream outputs). | `{{ config(model_type="template") }}` |
|
|
72
|
+
| **`execute_python`** | Runs rendered template as Python; captures `stdout` or `output` variable. | `{{ config(model_type="execute_python") }}` |
|
|
73
|
+
| **`agent`** | Hands the rendered prompt as a task to a coding agent (`mini-swe-agent`). | `{{ config(model_type="agent", agent_dir="./repo") }}` |
|
|
74
|
+
|
|
75
|
+
*(Optional extra model kinds like `python_modal`, `agent_modal`, and `agent37` are available via `pip install "promptmap[extras]"`).*
|
|
76
|
+
|
|
77
|
+
### 3. Tests (`tests/*.prompt`)
|
|
78
|
+
|
|
79
|
+
Run `promptmap test` to validate the latest run's outputs against your expectations:
|
|
80
|
+
|
|
81
|
+
* **LLM-as-a-judge:** Prompt tests in `tests/*.prompt` pass when the LLM returns `{"results": "pass"}`.
|
|
82
|
+
* **Deterministic checks:** Compare output fields directly in YAML using `expect` (no LLM judge needed).
|
|
83
|
+
|
|
84
|
+
### 4. Visualizing & serving (`promptmap docs` & `promptmap serve`)
|
|
85
|
+
|
|
86
|
+
Inspect prompt graph visually:
|
|
87
|
+
|
|
88
|
+
* **`promptmap docs`** generates a self-contained HTML report of every run—showing rendered prompts, model outputs, token usage, and test results across the DAG.
|
|
89
|
+
* **`promptmap serve`** starts a local server to interactively explore the DAG, trigger runs or inspect previous results.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Documentation
|
|
94
|
+
|
|
95
|
+
| Category | Guides |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| **Inputs & Data** | [Passing variables (`promptdata`)](docs/INTERMEDIATE_USAGE.md#passing-variables-to-templates-promptdata) · [Passing files (`promptfiles`)](docs/INTERMEDIATE_USAGE.md#passing-files-to-models-promptfiles) · [RAG inside prompts](docs/INTERMEDIATE_USAGE.md#rag-inside-prompts-ragpy) · [Global instructions](docs/ADVANCED_USAGE.md#global-instructions-globalprompt) |
|
|
98
|
+
| **Workflow & DAG** | [Grouping models in folders](docs/INTERMEDIATE_USAGE.md#grouping-models-in-folders) · [Map & reduce (`each=`)](docs/ADVANCED_USAGE.md#map-and-reduce-each) · [Dynamically skipping models](docs/INTERMEDIATE_USAGE.md#how-to-dynamically-skip-a-model) · [Models that produce files](docs/ADVANCED_USAGE.md#models-that-produce-files-promptmapfile-promptmapdir-promptmapoutput) · [Run IDs & iterating on the last answer (`previous`)](docs/ADVANCED_USAGE.md#run-ids-and-iterating-on-the-last-answer-previous) |
|
|
99
|
+
| **Testing & Eval** | [Writing tests](docs/INTERMEDIATE_USAGE.md#promptmap-tests) · [Validation](docs/INTERMEDIATE_USAGE.md#validation-validation) · [Bulk YAML testing & `expect`](docs/ADVANCED_USAGE.md#bulk-testing-with-yaml-cases-promptmap-test) |
|
|
100
|
+
| **CLI, UI & API** | [`promptmap docs` (HTML report)](docs/INTERMEDIATE_USAGE.md#promptmap-docs) · [`promptmap serve` & HTTP server](docs/ADVANCED_USAGE.md#promptmap-serve) · [`promptmap ls`](docs/ADVANCED_USAGE.md#promptmap-ls) · [Python API & callbacks](docs/ADVANCED_USAGE.md#python-api) · [Token usage](docs/ADVANCED_USAGE.md#token-usage-promptmapllmresult) |
|
|
101
|
+
| **Customization** | [Custom LLM backend (`client.py`)](docs/INTERMEDIATE_USAGE.md#customising-the-llm-backend-clientpy) · [Output format & `config()`](docs/ADVANCED_USAGE.md#output-format-config-config) · [Agent models](docs/ADVANCED_USAGE.md#agent-models-model_typeagent) · [Custom model kinds](docs/ADVANCED_USAGE.md#writing-your-own-model-kind-model_type) · [Architecture](docs/ARCHITECTURE.md) |
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# promptmap
|
|
2
|
+
|
|
3
|
+
A **data-engineering-inspired** prompt orchestration tool for LLMs (think `dbt` for prompts).
|
|
4
|
+
|
|
5
|
+
**Why `promptmap`?** Treating complex LLM workflows as giant one-shot prompts is like navigating a city by staring at your feet, one wrong turn and you're lost. `promptmap` turns your prompts into a visual map of connected hypotheses (`models`) and reality-check landmarks (`tests`), with built-in tools to inspect runs (`promptmap docs`).
|
|
6
|
+
|
|
7
|
+
**Mechanically, this means you:** write modular prompts in Jinja2, chain them into a dependency graph with `ref()`, and validate outputs against reality with `promptmap test`. Iterate on complex LLM workflows without losing context or rewriting from scratch. *(Read more on the philosophy in [motivation.md](motivation.md)).*
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv tool install promptmap --with anthropic # or: openai | gemini
|
|
17
|
+
promptmap init --provider anthropic # or: openai | gemini
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Edit your `.prompt` files in `map/`, then run the pipeline, test expectations, and inspect the run:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
export ANTHROPIC_API_KEY=your_key_here
|
|
25
|
+
|
|
26
|
+
promptmap run # Execute the prompt DAG
|
|
27
|
+
promptmap test # Validate outputs against tests/
|
|
28
|
+
promptmap docs # Generate HTML report of the run and run history
|
|
29
|
+
promptmap serve # Launch the interactive local UI / HTTP server
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Core concepts
|
|
35
|
+
|
|
36
|
+
### 1. Models (`map/*.prompt`)
|
|
37
|
+
|
|
38
|
+
Each `.prompt` file is a node in your DAG. Use `ref('model_name')` to inject the output of an upstream model:
|
|
39
|
+
|
|
40
|
+
```jinja
|
|
41
|
+
{# map/outline.prompt #}
|
|
42
|
+
Based on this topic, create a detailed outline:
|
|
43
|
+
|
|
44
|
+
{{ ref('topic') }}
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. Model types (`config(model_type=...)`)
|
|
49
|
+
|
|
50
|
+
Not every node in your pipeline needs to be a standard LLM call. Override `model_type` at the top of a `.prompt` file for deterministic steps, formatting, or coding agents:
|
|
51
|
+
|
|
52
|
+
| Model Type | What it does | Declaration |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| **LLM** *(default)* | Sends the rendered Jinja prompt to your configured LLM. | Standard `.prompt` file |
|
|
55
|
+
| **`template`** | Renders Jinja directly with no LLM call (for reshaping upstream outputs). | `{{ config(model_type="template") }}` |
|
|
56
|
+
| **`execute_python`** | Runs rendered template as Python; captures `stdout` or `output` variable. | `{{ config(model_type="execute_python") }}` |
|
|
57
|
+
| **`agent`** | Hands the rendered prompt as a task to a coding agent (`mini-swe-agent`). | `{{ config(model_type="agent", agent_dir="./repo") }}` |
|
|
58
|
+
|
|
59
|
+
*(Optional extra model kinds like `python_modal`, `agent_modal`, and `agent37` are available via `pip install "promptmap[extras]"`).*
|
|
60
|
+
|
|
61
|
+
### 3. Tests (`tests/*.prompt`)
|
|
62
|
+
|
|
63
|
+
Run `promptmap test` to validate the latest run's outputs against your expectations:
|
|
64
|
+
|
|
65
|
+
* **LLM-as-a-judge:** Prompt tests in `tests/*.prompt` pass when the LLM returns `{"results": "pass"}`.
|
|
66
|
+
* **Deterministic checks:** Compare output fields directly in YAML using `expect` (no LLM judge needed).
|
|
67
|
+
|
|
68
|
+
### 4. Visualizing & serving (`promptmap docs` & `promptmap serve`)
|
|
69
|
+
|
|
70
|
+
Inspect prompt graph visually:
|
|
71
|
+
|
|
72
|
+
* **`promptmap docs`** generates a self-contained HTML report of every run—showing rendered prompts, model outputs, token usage, and test results across the DAG.
|
|
73
|
+
* **`promptmap serve`** starts a local server to interactively explore the DAG, trigger runs or inspect previous results.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Documentation
|
|
78
|
+
|
|
79
|
+
| Category | Guides |
|
|
80
|
+
| --- | --- |
|
|
81
|
+
| **Inputs & Data** | [Passing variables (`promptdata`)](docs/INTERMEDIATE_USAGE.md#passing-variables-to-templates-promptdata) · [Passing files (`promptfiles`)](docs/INTERMEDIATE_USAGE.md#passing-files-to-models-promptfiles) · [RAG inside prompts](docs/INTERMEDIATE_USAGE.md#rag-inside-prompts-ragpy) · [Global instructions](docs/ADVANCED_USAGE.md#global-instructions-globalprompt) |
|
|
82
|
+
| **Workflow & DAG** | [Grouping models in folders](docs/INTERMEDIATE_USAGE.md#grouping-models-in-folders) · [Map & reduce (`each=`)](docs/ADVANCED_USAGE.md#map-and-reduce-each) · [Dynamically skipping models](docs/INTERMEDIATE_USAGE.md#how-to-dynamically-skip-a-model) · [Models that produce files](docs/ADVANCED_USAGE.md#models-that-produce-files-promptmapfile-promptmapdir-promptmapoutput) · [Run IDs & iterating on the last answer (`previous`)](docs/ADVANCED_USAGE.md#run-ids-and-iterating-on-the-last-answer-previous) |
|
|
83
|
+
| **Testing & Eval** | [Writing tests](docs/INTERMEDIATE_USAGE.md#promptmap-tests) · [Validation](docs/INTERMEDIATE_USAGE.md#validation-validation) · [Bulk YAML testing & `expect`](docs/ADVANCED_USAGE.md#bulk-testing-with-yaml-cases-promptmap-test) |
|
|
84
|
+
| **CLI, UI & API** | [`promptmap docs` (HTML report)](docs/INTERMEDIATE_USAGE.md#promptmap-docs) · [`promptmap serve` & HTTP server](docs/ADVANCED_USAGE.md#promptmap-serve) · [`promptmap ls`](docs/ADVANCED_USAGE.md#promptmap-ls) · [Python API & callbacks](docs/ADVANCED_USAGE.md#python-api) · [Token usage](docs/ADVANCED_USAGE.md#token-usage-promptmapllmresult) |
|
|
85
|
+
| **Customization** | [Custom LLM backend (`client.py`)](docs/INTERMEDIATE_USAGE.md#customising-the-llm-backend-clientpy) · [Output format & `config()`](docs/ADVANCED_USAGE.md#output-format-config-config) · [Agent models](docs/ADVANCED_USAGE.md#agent-models-model_typeagent) · [Custom model kinds](docs/ADVANCED_USAGE.md#writing-your-own-model-kind-model_type) · [Architecture](docs/ARCHITECTURE.md) |
|
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
"""promptmap — dbt-inspired LLM prompt orchestration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from importlib.metadata import PackageNotFoundError, version as package_version
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Awaitable, Callable, Mapping
|
|
10
|
+
|
|
11
|
+
from promptmap.executor.parser_initial import (
|
|
12
|
+
UnknownConfigKeyWarning,
|
|
13
|
+
known_config_keys,
|
|
14
|
+
register_config_keys,
|
|
15
|
+
)
|
|
16
|
+
from promptmap.classifier import classifier_cache_check, systemone_classifier
|
|
17
|
+
from promptmap.executor.graph import UnknownModelTypeError
|
|
18
|
+
from promptmap.model_spec import ModelSpec
|
|
19
|
+
from promptmap.model_types import (
|
|
20
|
+
ModelCall,
|
|
21
|
+
ModelKind,
|
|
22
|
+
get_model_kind,
|
|
23
|
+
known_model_kinds,
|
|
24
|
+
model_kind,
|
|
25
|
+
register_model_kind,
|
|
26
|
+
)
|
|
27
|
+
from promptmap.files import (
|
|
28
|
+
BlobStore,
|
|
29
|
+
Dir,
|
|
30
|
+
File,
|
|
31
|
+
MemoryBlobStore,
|
|
32
|
+
Output,
|
|
33
|
+
SQLiteBlobStore,
|
|
34
|
+
)
|
|
35
|
+
from promptmap.secrets import Secrets
|
|
36
|
+
from promptmap.storage.base import StorageBackend
|
|
37
|
+
from promptmap.tokens import LLMResult
|
|
38
|
+
from promptmap.executor.executor import ModelRunResult
|
|
39
|
+
from promptmap.executor.parser_model import PreviousRun
|
|
40
|
+
from promptmap.types import PromptFile, PromptModelsDict
|
|
41
|
+
|
|
42
|
+
# Registers the built-in kinds ("", template, execute_python, agent). Imported here,
|
|
43
|
+
# after the registry exists, so that every entry point into promptmap has them without
|
|
44
|
+
# the registry needing a lazy-import hook.
|
|
45
|
+
import promptmap.executor.builtin_kinds # noqa: E402,F401
|
|
46
|
+
|
|
47
|
+
__all__ = [
|
|
48
|
+
"run",
|
|
49
|
+
"async_run",
|
|
50
|
+
"ModelSpec",
|
|
51
|
+
"ModelKind",
|
|
52
|
+
"ModelCall",
|
|
53
|
+
"model_kind",
|
|
54
|
+
"register_model_kind",
|
|
55
|
+
"get_model_kind",
|
|
56
|
+
"known_model_kinds",
|
|
57
|
+
"register_config_keys",
|
|
58
|
+
"known_config_keys",
|
|
59
|
+
"UnknownConfigKeyWarning",
|
|
60
|
+
"UnknownModelTypeError",
|
|
61
|
+
"systemone_classifier",
|
|
62
|
+
"classifier_cache_check",
|
|
63
|
+
"ModelStatus",
|
|
64
|
+
"ModelError",
|
|
65
|
+
"PromptModelsDict",
|
|
66
|
+
"PromptFile",
|
|
67
|
+
"StorageBackend",
|
|
68
|
+
"Secrets",
|
|
69
|
+
"File",
|
|
70
|
+
"Dir",
|
|
71
|
+
"Output",
|
|
72
|
+
"LLMResult",
|
|
73
|
+
"ModelRunResult",
|
|
74
|
+
"PreviousRun",
|
|
75
|
+
"BlobStore",
|
|
76
|
+
"MemoryBlobStore",
|
|
77
|
+
"SQLiteBlobStore",
|
|
78
|
+
"__version__",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _version_from_pyproject() -> str:
|
|
83
|
+
pyproject = Path(__file__).resolve().parent.parent / "pyproject.toml"
|
|
84
|
+
contents = pyproject.read_text(encoding="utf-8")
|
|
85
|
+
project_block = re.search(r"(?ms)^\[project\]\n(.*?)(?:^\[|\Z)", contents)
|
|
86
|
+
if project_block is None:
|
|
87
|
+
raise RuntimeError("Could not find [project] table in pyproject.toml")
|
|
88
|
+
|
|
89
|
+
match = re.search(r'^version\s*=\s*"([^"]+)"\s*$', project_block.group(1), re.MULTILINE)
|
|
90
|
+
if match is None:
|
|
91
|
+
raise RuntimeError("Could not find project.version in pyproject.toml")
|
|
92
|
+
return match.group(1)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _detect_version() -> str:
|
|
96
|
+
try:
|
|
97
|
+
return package_version("promptmap")
|
|
98
|
+
except PackageNotFoundError:
|
|
99
|
+
return _version_from_pyproject()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
__version__ = _detect_version()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class ModelStatus(Enum):
|
|
106
|
+
"""Returned in the promptmap.run() dict for models that produced no LLM output."""
|
|
107
|
+
SKIPPED = "skipped" # upstream dependency failed → model was not run
|
|
108
|
+
ERROR = "error" # LLM call or render raised an exception (legacy)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class ModelError:
|
|
112
|
+
"""Returned by promptmap.run() when a model fails; carries the exception message."""
|
|
113
|
+
def __init__(self, message: str) -> None:
|
|
114
|
+
self.message = message
|
|
115
|
+
|
|
116
|
+
def __str__(self) -> str:
|
|
117
|
+
return f"error: {self.message}"
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
async def async_run(
|
|
121
|
+
map_dir: str = "map",
|
|
122
|
+
models_from_dict: PromptModelsDict | dict[str, str] | None = None,
|
|
123
|
+
select: list[str] | None = None,
|
|
124
|
+
llm_call: Callable[[str], str | Awaitable[str]] | None = None,
|
|
125
|
+
rag_call: Callable[..., list] | None = None,
|
|
126
|
+
verbose: bool = True,
|
|
127
|
+
promptdata: dict | None = None,
|
|
128
|
+
promptfiles: dict[str, PromptFile] | None = None,
|
|
129
|
+
validation_dir: str | None = "validation",
|
|
130
|
+
tests_dir: str | None = "tests",
|
|
131
|
+
storage_backend: StorageBackend | None = None,
|
|
132
|
+
global_instruction: str | Callable[[], str] | None = None,
|
|
133
|
+
blob_store: BlobStore | None = None,
|
|
134
|
+
on_model_start: Callable[[str], None] | None = None,
|
|
135
|
+
on_model_done: Callable[[ModelRunResult], None] | None = None,
|
|
136
|
+
secrets: Mapping[str, str] | None = None,
|
|
137
|
+
cache_check: Callable[..., bool | Awaitable[bool]] | None = None,
|
|
138
|
+
):
|
|
139
|
+
"""
|
|
140
|
+
Execute prompt models as a Python library call.
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
map_dir:
|
|
145
|
+
Path to the directory containing *.prompt files.
|
|
146
|
+
select:
|
|
147
|
+
Optional list of model names to run. All upstream dependencies are
|
|
148
|
+
also executed fresh; the prompt cache makes unchanged nodes instant.
|
|
149
|
+
llm_call:
|
|
150
|
+
Optional function ``(prompt: str) -> str`` to use as the LLM backend.
|
|
151
|
+
Falls back to ``llm_call`` in client.py alongside *map_dir*; with
|
|
152
|
+
neither, the run fails with a FileNotFoundError naming the fix.
|
|
153
|
+
rag_call:
|
|
154
|
+
Optional function ``(*args) -> list | str`` to back
|
|
155
|
+
``return_list_RAG_results()`` in templates.
|
|
156
|
+
Falls back to ``map/rag.py::do_RAG`` if present.
|
|
157
|
+
verbose:
|
|
158
|
+
Print a dbt-style progress log to stdout (default: True).
|
|
159
|
+
promptdata:
|
|
160
|
+
Optional dict of runtime variables. Access them in templates via
|
|
161
|
+
``{{ promptdata('key') }}``.
|
|
162
|
+
promptfiles:
|
|
163
|
+
Optional dict mapping name → file reference. Models that declare
|
|
164
|
+
``promptfiles: name`` in their config block will receive these as a
|
|
165
|
+
list passed to ``llm_call(prompt, files=[…])``.
|
|
166
|
+
Values may be a **string path**, a :class:`pathlib.Path`, or any
|
|
167
|
+
**binary file-like object** (``open(..., "rb")``, ``io.BytesIO``,
|
|
168
|
+
etc.) — whatever your ``llm_call`` implementation accepts.
|
|
169
|
+
tests_dir:
|
|
170
|
+
The tests directory, hashed into the run's ``map_hash`` so a changed
|
|
171
|
+
test changes the map's ID (see :mod:`promptmap.run_ids`). Ignored
|
|
172
|
+
for ``models_from_dict`` runs.
|
|
173
|
+
global_instruction:
|
|
174
|
+
Optional prompt text rendered into **every** model prompt — promptmap's
|
|
175
|
+
analogue of dbt's query-comment. Either a string or a zero-argument
|
|
176
|
+
callable returning one. Falls back to a ``global.prompt`` file
|
|
177
|
+
alongside ``map_dir`` when not given (file-based runs only).
|
|
178
|
+
|
|
179
|
+
The text is itself a Jinja template rendered with each model's own
|
|
180
|
+
context, so ``promptdata()``, ``model.name`` and ``was_skipped()``
|
|
181
|
+
work inside it; ``ref()`` does not, since that would make every model
|
|
182
|
+
depend on the referenced one. Reference ``{{ prompt }}`` to place the
|
|
183
|
+
model body explicitly, otherwise the instruction is prepended.
|
|
184
|
+
|
|
185
|
+
Individual models opt out with
|
|
186
|
+
``{{ config(global_instruction=False) }}``, and ``execute_python``
|
|
187
|
+
models never receive it.
|
|
188
|
+
blob_store:
|
|
189
|
+
Where the bytes of file outputs (:class:`File`, :class:`Dir`,
|
|
190
|
+
:class:`Output`) are kept. Falls back to ``blob_store`` in client.py,
|
|
191
|
+
then to the storage backend's own store (the SQLite database).
|
|
192
|
+
on_model_start:
|
|
193
|
+
Optional callback ``(model_name: str) -> None`` fired just before a
|
|
194
|
+
model starts executing. Use it for live progress in your own code.
|
|
195
|
+
on_model_done:
|
|
196
|
+
Optional callback ``(result: ModelRunResult) -> None`` fired when a
|
|
197
|
+
model finishes, whether it succeeded, errored or was skipped.
|
|
198
|
+
``result.status`` is ``"success"``, ``"error"`` or ``"skipped"``.
|
|
199
|
+
secrets:
|
|
200
|
+
Optional dict of run-scoped credentials (API keys, tokens). Model
|
|
201
|
+
kinds read them as ``call.secrets["NAME"]``; an ``llm_call`` that
|
|
202
|
+
declares a ``secrets`` parameter receives them as well, so one
|
|
203
|
+
``llm_call`` can serve a different key per run. They are never
|
|
204
|
+
rendered into templates, part of the cache key, stored, or shown in a
|
|
205
|
+
repr — changing a key's value still gets cache hits.
|
|
206
|
+
cache_check:
|
|
207
|
+
Optional function ``(prompt: str, previous: PreviousRun) -> bool``
|
|
208
|
+
asked on an exact prompt-cache miss whether the model's last
|
|
209
|
+
successful result (``previous.prompt``, ``previous.output``) is still
|
|
210
|
+
good enough for the new *prompt*. Return True to reuse it as a cache
|
|
211
|
+
hit — e.g. a cheap judge model deciding whether the prompt changed
|
|
212
|
+
meaningfully. Sync or async; may also declare ``config``,
|
|
213
|
+
``secrets`` and ``model`` parameters. Falls back to ``cache_check``
|
|
214
|
+
in client.py.
|
|
215
|
+
|
|
216
|
+
Returns
|
|
217
|
+
-------
|
|
218
|
+
``dict`` keyed by model name. Each value is the model's output string —
|
|
219
|
+
or, for a model that produced files, its :class:`File` / :class:`Dir` /
|
|
220
|
+
:class:`Output` value (or the dict/list holding them) — or
|
|
221
|
+
:class:`ModelStatus.SKIPPED` when an upstream model failed, or a
|
|
222
|
+
:class:`ModelError` carrying the message when that model itself failed.
|
|
223
|
+
|
|
224
|
+
The full per-model detail — rendered prompt, timing, cache hit — is not in
|
|
225
|
+
this return value; it is written to storage and read back by ``promptmap test``
|
|
226
|
+
and ``promptmap docs``.
|
|
227
|
+
"""
|
|
228
|
+
import time
|
|
229
|
+
from datetime import datetime
|
|
230
|
+
|
|
231
|
+
from promptmap.executor.executor import execute_run, ModelRunResult
|
|
232
|
+
from promptmap.executor.graph import (
|
|
233
|
+
load_models,
|
|
234
|
+
build_models_from_dict,
|
|
235
|
+
build_dag,
|
|
236
|
+
)
|
|
237
|
+
if storage_backend is None:
|
|
238
|
+
from promptmap.storage.sqlite import SQLiteStorageBackend
|
|
239
|
+
storage_backend = SQLiteStorageBackend()
|
|
240
|
+
|
|
241
|
+
console = None
|
|
242
|
+
if verbose:
|
|
243
|
+
from rich.console import Console
|
|
244
|
+
console = Console(highlight=False, soft_wrap=True)
|
|
245
|
+
|
|
246
|
+
def ts() -> str:
|
|
247
|
+
return datetime.now().strftime("%H:%M:%S")
|
|
248
|
+
|
|
249
|
+
def log(msg: str) -> None:
|
|
250
|
+
if verbose:
|
|
251
|
+
console.print(f"[dim]{ts()}[/dim] {msg}")
|
|
252
|
+
|
|
253
|
+
storage_backend.init_db()
|
|
254
|
+
|
|
255
|
+
# client.py is imported before the models are parsed: it is where a project
|
|
256
|
+
# registers its own model types, and an unregistered type looks like a typo.
|
|
257
|
+
if models_from_dict is None:
|
|
258
|
+
from promptmap.llm import try_load_client_module
|
|
259
|
+
|
|
260
|
+
try_load_client_module(map_dir)
|
|
261
|
+
|
|
262
|
+
raw = models_from_dict.models if isinstance(models_from_dict, PromptModelsDict) else models_from_dict
|
|
263
|
+
all_models = build_models_from_dict(raw) if models_from_dict is not None else load_models(map_dir)
|
|
264
|
+
|
|
265
|
+
if select:
|
|
266
|
+
import networkx as nx
|
|
267
|
+
dag = build_dag(all_models)
|
|
268
|
+
to_run: set[str] = set(select)
|
|
269
|
+
for name in select:
|
|
270
|
+
to_run.update(nx.ancestors(dag, name))
|
|
271
|
+
ordered = [all_models[name] for name in to_run if name in all_models]
|
|
272
|
+
else:
|
|
273
|
+
ordered = list(all_models.values())
|
|
274
|
+
|
|
275
|
+
git_sha = None
|
|
276
|
+
if models_from_dict is None:
|
|
277
|
+
import subprocess
|
|
278
|
+
try:
|
|
279
|
+
git_sha = subprocess.check_output(
|
|
280
|
+
["git", "rev-parse", "--short", "HEAD"], stderr=subprocess.DEVNULL
|
|
281
|
+
).decode().strip()
|
|
282
|
+
except Exception:
|
|
283
|
+
git_sha = None
|
|
284
|
+
|
|
285
|
+
# Resolve LLM and RAG backends from user files if not explicitly provided
|
|
286
|
+
if llm_call is None:
|
|
287
|
+
if models_from_dict is not None:
|
|
288
|
+
raise ValueError(
|
|
289
|
+
"llm_call must be provided when using models_from_dict. "
|
|
290
|
+
"Inline runs do not auto-load client.py from disk."
|
|
291
|
+
)
|
|
292
|
+
from promptmap.llm import resolve_llm_call
|
|
293
|
+
llm_call = resolve_llm_call(map_dir)
|
|
294
|
+
if rag_call is None and models_from_dict is None:
|
|
295
|
+
from promptmap.rag import resolve_rag_call
|
|
296
|
+
rag_call = resolve_rag_call(map_dir)
|
|
297
|
+
if blob_store is None and models_from_dict is None:
|
|
298
|
+
from promptmap.llm import resolve_blob_store
|
|
299
|
+
blob_store = resolve_blob_store(map_dir)
|
|
300
|
+
if cache_check is None and models_from_dict is None:
|
|
301
|
+
from promptmap.llm import resolve_cache_check
|
|
302
|
+
cache_check = resolve_cache_check(map_dir)
|
|
303
|
+
|
|
304
|
+
# Global instruction: explicit argument wins, else global.prompt on disk.
|
|
305
|
+
from promptmap.global_instruction import (
|
|
306
|
+
coerce_global_instruction,
|
|
307
|
+
resolve_global_instruction,
|
|
308
|
+
)
|
|
309
|
+
global_instruction = coerce_global_instruction(global_instruction)
|
|
310
|
+
if global_instruction is None and models_from_dict is None:
|
|
311
|
+
global_instruction = resolve_global_instruction(map_dir)
|
|
312
|
+
|
|
313
|
+
# Load per-model validators from validation_dir (optional)
|
|
314
|
+
validators = None
|
|
315
|
+
if validation_dir is not None and models_from_dict is None:
|
|
316
|
+
from promptmap.validator import load_validators
|
|
317
|
+
validators = load_validators(validation_dir)
|
|
318
|
+
|
|
319
|
+
run_id = storage_backend.create_run(
|
|
320
|
+
model_count=len(ordered),
|
|
321
|
+
git_sha=git_sha,
|
|
322
|
+
)
|
|
323
|
+
from promptmap.run_ids import record_run_ids
|
|
324
|
+
|
|
325
|
+
record_run_ids(
|
|
326
|
+
storage_backend,
|
|
327
|
+
run_id,
|
|
328
|
+
all_models.values(),
|
|
329
|
+
ordered,
|
|
330
|
+
tests_dir=tests_dir if models_from_dict is None else None,
|
|
331
|
+
global_instruction=global_instruction,
|
|
332
|
+
promptdata=promptdata,
|
|
333
|
+
promptfiles=promptfiles,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
total = len(ordered)
|
|
337
|
+
|
|
338
|
+
if verbose:
|
|
339
|
+
console.print(
|
|
340
|
+
f"[dim]{ts()}[/dim] Running with [bold cyan]promptmap[/bold cyan]={__version__}"
|
|
341
|
+
)
|
|
342
|
+
console.print(
|
|
343
|
+
f"[dim]{ts()}[/dim] Found [bold]{total}[/bold] prompt model{'s' if total != 1 else ''}"
|
|
344
|
+
+ (f" [dim](select: {sorted(select)})[/dim]" if select else "")
|
|
345
|
+
)
|
|
346
|
+
if git_sha:
|
|
347
|
+
console.print(f"[dim]{ts()}[/dim] git SHA [dim]{git_sha}[/dim]")
|
|
348
|
+
console.print(f"[dim]{ts()}[/dim]")
|
|
349
|
+
|
|
350
|
+
completed: list[ModelRunResult] = []
|
|
351
|
+
model_start_times: dict[str, float] = {}
|
|
352
|
+
|
|
353
|
+
def _log_model_line(idx: int, verb: str, name: str, badge: str) -> None:
|
|
354
|
+
prefix = f"{idx} of {total} {verb} prompt model {name} "
|
|
355
|
+
dots = "." * max(2, 50 - len(prefix))
|
|
356
|
+
if verbose:
|
|
357
|
+
console.print(f"[dim]{ts()}[/dim] [bold]{idx} of {total}[/bold] {verb} prompt model [cyan]{name}[/cyan] {dots} {badge}")
|
|
358
|
+
|
|
359
|
+
def on_start(name: str) -> None:
|
|
360
|
+
idx = len(completed) + 1
|
|
361
|
+
model_start_times[name] = time.monotonic()
|
|
362
|
+
_log_model_line(idx, "START", name, "[[yellow]RUN[/yellow]]")
|
|
363
|
+
if on_model_start:
|
|
364
|
+
on_model_start(name)
|
|
365
|
+
|
|
366
|
+
def on_done(result: ModelRunResult) -> None:
|
|
367
|
+
completed.append(result)
|
|
368
|
+
idx = len(completed)
|
|
369
|
+
elapsed = time.monotonic() - model_start_times.get(result.model_name, 0)
|
|
370
|
+
if result.status == "success":
|
|
371
|
+
cached = " cached" if result.cached else ""
|
|
372
|
+
_log_model_line(idx, "OK ", result.model_name, f"[[green]OK{cached} in {elapsed:.2f}s[/green]]")
|
|
373
|
+
elif result.status == "skipped":
|
|
374
|
+
_log_model_line(idx, "SKIP ", result.model_name, "[[yellow]SKIP[/yellow]]")
|
|
375
|
+
else:
|
|
376
|
+
_log_model_line(idx, "ERR ", result.model_name, "[[red]ERROR[/red]]")
|
|
377
|
+
if verbose:
|
|
378
|
+
console.print(f" [dim]{result.error}[/dim]")
|
|
379
|
+
if on_model_done:
|
|
380
|
+
on_model_done(result)
|
|
381
|
+
|
|
382
|
+
run_start = time.monotonic()
|
|
383
|
+
|
|
384
|
+
results = await execute_run(
|
|
385
|
+
run_id=run_id,
|
|
386
|
+
ordered_models=ordered,
|
|
387
|
+
storage_backend=storage_backend,
|
|
388
|
+
llm_call=llm_call,
|
|
389
|
+
rag_call=rag_call,
|
|
390
|
+
on_model_start=on_start,
|
|
391
|
+
on_model_done=on_done,
|
|
392
|
+
promptdata=promptdata,
|
|
393
|
+
promptfiles=promptfiles,
|
|
394
|
+
validators=validators or None,
|
|
395
|
+
global_instruction=global_instruction,
|
|
396
|
+
blob_store=blob_store,
|
|
397
|
+
secrets=secrets,
|
|
398
|
+
cache_check=cache_check,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
errors = sum(1 for r in results if r.status == "error")
|
|
402
|
+
skipped = sum(1 for r in results if r.status == "skipped")
|
|
403
|
+
successes = sum(1 for r in results if r.status == "success")
|
|
404
|
+
final_status = "success" if errors == 0 else ("partial" if successes > 0 else "error")
|
|
405
|
+
storage_backend.finish_run(run_id, final_status)
|
|
406
|
+
total_elapsed = time.monotonic() - run_start
|
|
407
|
+
|
|
408
|
+
if verbose:
|
|
409
|
+
console.print(f"[dim]{ts()}[/dim]")
|
|
410
|
+
console.print(
|
|
411
|
+
f"[dim]{ts()}[/dim] Finished running {total} prompt model{'s' if total != 1 else ''} "
|
|
412
|
+
f"in [bold]{total_elapsed:.2f}s[/bold]. "
|
|
413
|
+
+ ("[green]Completed successfully[/green]" if errors == 0 else "[red]Completed with errors[/red]")
|
|
414
|
+
)
|
|
415
|
+
pass_str = f"[green]PASS={successes}[/green]"
|
|
416
|
+
error_str = f"[red]ERROR={errors}[/red]"
|
|
417
|
+
skip_str = f"[yellow]SKIP={skipped}[/yellow]"
|
|
418
|
+
total_str = f"TOTAL={total}"
|
|
419
|
+
console.print(f"[dim]{ts()}[/dim] {pass_str} {error_str} {skip_str} {total_str}")
|
|
420
|
+
|
|
421
|
+
def _value(r):
|
|
422
|
+
if r.status == "skipped":
|
|
423
|
+
return ModelStatus.SKIPPED
|
|
424
|
+
if r.status == "error":
|
|
425
|
+
return ModelError(r.error or "error")
|
|
426
|
+
from promptmap.files import contains_files
|
|
427
|
+
|
|
428
|
+
if contains_files(r.value) or isinstance(r.value, str):
|
|
429
|
+
return r.value
|
|
430
|
+
return r.llm_output
|
|
431
|
+
|
|
432
|
+
return {r.model_name: _value(r) for r in results}
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def run(
|
|
436
|
+
map_dir: str = "map",
|
|
437
|
+
models_from_dict: "PromptModelsDict | dict[str, str] | None" = None,
|
|
438
|
+
select: "list[str] | None" = None,
|
|
439
|
+
llm_call: "Callable[[str], str | Awaitable[str]] | None" = None,
|
|
440
|
+
rag_call: "Callable[..., list] | None" = None,
|
|
441
|
+
verbose: bool = True,
|
|
442
|
+
promptdata: "dict | None" = None,
|
|
443
|
+
promptfiles: "dict[str, PromptFile] | None" = None,
|
|
444
|
+
validation_dir: "str | None" = "validation",
|
|
445
|
+
tests_dir: "str | None" = "tests",
|
|
446
|
+
storage_backend: "StorageBackend | None" = None,
|
|
447
|
+
global_instruction: "str | Callable[[], str] | None" = None,
|
|
448
|
+
blob_store: "BlobStore | None" = None,
|
|
449
|
+
on_model_start: "Callable[[str], None] | None" = None,
|
|
450
|
+
on_model_done: "Callable[[ModelRunResult], None] | None" = None,
|
|
451
|
+
secrets: "Mapping[str, str] | None" = None,
|
|
452
|
+
cache_check: "Callable[..., bool | Awaitable[bool]] | None" = None,
|
|
453
|
+
):
|
|
454
|
+
"""Run prompt models synchronously.
|
|
455
|
+
|
|
456
|
+
Wraps :func:`async_run` in ``asyncio.run()``; same arguments, same dict
|
|
457
|
+
return value. Call ``async_run`` directly from inside an event loop.
|
|
458
|
+
"""
|
|
459
|
+
import asyncio
|
|
460
|
+
|
|
461
|
+
return asyncio.run(async_run(
|
|
462
|
+
map_dir=map_dir,
|
|
463
|
+
models_from_dict=models_from_dict,
|
|
464
|
+
select=select,
|
|
465
|
+
llm_call=llm_call,
|
|
466
|
+
rag_call=rag_call,
|
|
467
|
+
verbose=verbose,
|
|
468
|
+
promptdata=promptdata,
|
|
469
|
+
promptfiles=promptfiles,
|
|
470
|
+
validation_dir=validation_dir,
|
|
471
|
+
tests_dir=tests_dir,
|
|
472
|
+
storage_backend=storage_backend,
|
|
473
|
+
global_instruction=global_instruction,
|
|
474
|
+
blob_store=blob_store,
|
|
475
|
+
on_model_start=on_model_start,
|
|
476
|
+
on_model_done=on_model_done,
|
|
477
|
+
secrets=secrets,
|
|
478
|
+
cache_check=cache_check,
|
|
479
|
+
))
|