thoughtflow 0.2.0__tar.gz → 0.2.2__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.
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/CHANGELOG.md +32 -5
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/PKG-INFO +80 -3
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/README.md +79 -2
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/pyproject.toml +1 -1
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/__init__.py +8 -1
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/_util.py +264 -5
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/embed.py +27 -5
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/llm.py +188 -92
- thoughtflow-0.2.2/src/thoughtflow/llm_cache.py +211 -0
- thoughtflow-0.2.2/src/thoughtflow/llm_params.py +156 -0
- thoughtflow-0.2.2/src/thoughtflow/providers/README.md +15 -0
- thoughtflow-0.2.2/src/thoughtflow/providers/__init__.py +16 -0
- thoughtflow-0.2.2/src/thoughtflow/providers/cerebras.py +75 -0
- thoughtflow-0.2.2/src/thoughtflow/providers/groq.py +63 -0
- thoughtflow-0.2.2/src/thoughtflow/providers/openrouter.py +36 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/thought.py +368 -69
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/thoughts/plan.py +69 -20
- thoughtflow-0.2.2/tests/integration/smoke_helpers.py +104 -0
- thoughtflow-0.2.2/tests/integration/test_embed_providers.py +123 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/integration/test_llm_providers.py +173 -2
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_actions.py +4 -2
- thoughtflow-0.2.2/tests/unit/test_foundation_agent_response.py +240 -0
- thoughtflow-0.2.2/tests/unit/test_llm_provider_integration.py +288 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_replay.py +6 -6
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_thought.py +1 -1
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/.gitignore +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/LICENSE +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/_cron_expr.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/action.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/__init__.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/_http.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/_substitution.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/ask.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/call.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/fetch.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/noop.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/notify.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/post.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/read_file.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/run.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/say.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/scrape.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/search.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/sleep.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/wait.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/actions/write_file.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/agent.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/agents/__init__.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/agents/planact.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/agents/react.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/agents/reflect.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/chat.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/chron.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/delegate.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/eval/__init__.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/eval/harness.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/mcp.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/memory.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/message.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/py.typed +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/this.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/thoughts/__init__.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/thoughts/decide.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/tool.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/src/thoughtflow/workflow.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/__init__.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/conftest.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/integration/__init__.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/integration/test_chron.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/integration/test_scrape.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/integration/test_search_providers.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/__init__.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_action.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_agent.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_chat.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_chron.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_delegate.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_embed.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_eval.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_llm.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_mcp.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_memory.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_message.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_this.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_tool.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_util.py +0 -0
- {thoughtflow-0.2.0 → thoughtflow-0.2.2}/tests/unit/test_workflow.py +0 -0
|
@@ -5,16 +5,42 @@ All notable changes to ThoughtFlow will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [0.2.2] - 2026-07-10
|
|
9
|
+
|
|
10
|
+
Production-hardening release: THOUGHT/PLAN parsing improvements (Foundation Agent feedback) plus unified LLM param pipeline, prompt caching, and native provider convenience classes.
|
|
9
11
|
|
|
10
12
|
### Added
|
|
11
|
-
-
|
|
13
|
+
- THOUGHT `on_failure=` hook and structured failure events on MEMORY (`{name}_last_error`, `{name}_attempts`)
|
|
14
|
+
- THOUGHT opt-in production lane: `validation_mode='rescue'`, `strict_schema=True`, `hold_msg=True`, `repair_target=`
|
|
15
|
+
- `valid_extract` optional `oneOf` schema dialect for branch-shaped responses
|
|
16
|
+
- PLAN `normalize_plan()` subclass hook and aggregate missing-param error messages
|
|
17
|
+
- LLM `last_error`, `last_usage`, opt-in `request_timeout=`, `on_usage=` callback
|
|
18
|
+
- Unified model-param pipeline (`split_call_params`) — transport keys never leak into JSON bodies
|
|
19
|
+
- Prompt caching: `cached_system()`, `cached_message()`, `cache=True` on messages; Anthropic `cache_control` wire adapter
|
|
20
|
+
- Native provider classes: `GroqLLM`, `CerebrasLLM`, `OpenRouterLLM`
|
|
21
|
+
- `docs/concepts/thought.md` — parse-validate pipeline guide
|
|
22
|
+
- Regression tests: `test_foundation_agent_response.py`, `test_llm_provider_integration.py`
|
|
23
|
+
- Example: `examples/scripts/15_thought_integration_test.py`
|
|
24
|
+
- Integration smoke tests: `tests/integration/test_llm_providers.py`, `test_embed_providers.py`
|
|
12
25
|
|
|
13
26
|
### Changed
|
|
14
|
-
-
|
|
27
|
+
- Validation callables may return bare `bool` (coerced to tuple automatically)
|
|
28
|
+
- Parse-failure and repair-suffix messages are model-friendly
|
|
29
|
+
- `parser='json'` uses balanced JSON extraction
|
|
30
|
+
- Execution events include `failed: true` when retries exhausted with error
|
|
31
|
+
- OpenAI-compatible choice extraction falls back to `message.reasoning` when `content` is empty
|
|
32
|
+
- Expanded `docs/concepts/llm.md`, `primitives/LLM.md`, `primitives/THOUGHT.md`, `primitives/PLAN.md`
|
|
15
33
|
|
|
16
34
|
### Fixed
|
|
17
|
-
-
|
|
35
|
+
- Bare-bool `validation=` lambdas no longer crash unpack
|
|
36
|
+
- PLAN param validation reports all missing params at once
|
|
37
|
+
- OpenRouter `referer`/`title` no longer leak into request JSON body
|
|
38
|
+
- EMBED Groq calls include User-Agent (Cloudflare HTTP 1010 without it)
|
|
39
|
+
- EMBED `text-embedding-004` aliases to `gemini-embedding-001`; `last_error` surfaced on failures
|
|
40
|
+
|
|
41
|
+
## [Unreleased]
|
|
42
|
+
|
|
43
|
+
_Nothing yet._
|
|
18
44
|
|
|
19
45
|
---
|
|
20
46
|
|
|
@@ -171,7 +197,8 @@ grown from MEMORY itself, with zero new concepts.
|
|
|
171
197
|
---
|
|
172
198
|
|
|
173
199
|
<!-- Release links -->
|
|
174
|
-
[Unreleased]: https://github.com/jrolf/thoughtflow/compare/v0.2.
|
|
200
|
+
[Unreleased]: https://github.com/jrolf/thoughtflow/compare/v0.2.2...HEAD
|
|
201
|
+
[0.2.2]: https://github.com/jrolf/thoughtflow/compare/v0.2.0...v0.2.2
|
|
175
202
|
[0.2.0]: https://github.com/jrolf/thoughtflow/compare/v0.1.3...v0.2.0
|
|
176
203
|
[0.1.3]: https://github.com/jrolf/thoughtflow/compare/v0.1.2...v0.1.3
|
|
177
204
|
[0.1.2]: https://github.com/jrolf/thoughtflow/compare/v0.1.1...v0.1.2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thoughtflow
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Powerful AI systems from simple parts. Composable cognitive primitives, event-sourced memory, and autonomous agents in pure Python with zero dependencies.
|
|
5
5
|
Project-URL: Homepage, https://github.com/jrolf/thoughtflow
|
|
6
6
|
Project-URL: Documentation, https://thoughtflow.dev
|
|
@@ -164,7 +164,7 @@ supply-chain anxiety. The core library uses only Python's standard library —
|
|
|
164
164
|
pip install --upgrade thoughtflow
|
|
165
165
|
|
|
166
166
|
# Pin to a specific version for stability
|
|
167
|
-
pip install thoughtflow==0.2.
|
|
167
|
+
pip install thoughtflow==0.2.2
|
|
168
168
|
|
|
169
169
|
# Check your installed version
|
|
170
170
|
python -c "import thoughtflow; print(thoughtflow.__version__)"
|
|
@@ -452,8 +452,18 @@ ThoughtFlow works with **any LLM provider** through a unified interface:
|
|
|
452
452
|
| **Groq** | `groq:model` | `groq:llama-3.1-70b-versatile` | Fast inference for open models |
|
|
453
453
|
| **Google Gemini** | `gemini:model` | `gemini:gemini-1.5-pro` | Gemini Pro, Flash, etc. |
|
|
454
454
|
| **OpenRouter** | `openrouter:model` | `openrouter:anthropic/claude-3-opus` | Access any model via OpenRouter |
|
|
455
|
+
| **Cerebras** | `openai:model` + `OpenAICompatibleLLM` | `CerebrasLLM("gpt-oss-120b", key=...)` | OpenAI-compatible; Cloudflare-safe defaults |
|
|
455
456
|
| **Ollama** | `ollama:model` | `ollama:llama3.2` | Local models, no API key needed |
|
|
456
457
|
|
|
458
|
+
**Native convenience classes** (v0.2.2+) wrap production edge cases while keeping the same `call()` API:
|
|
459
|
+
|
|
460
|
+
| Class | When to use |
|
|
461
|
+
|-------|-------------|
|
|
462
|
+
| `GroqLLM` | Qwen models on Groq — sets `reasoning_format=hidden` by default |
|
|
463
|
+
| `CerebrasLLM` | Cerebras inference — safe `User-Agent`, optional auth-key fallback |
|
|
464
|
+
| `OpenRouterLLM` | OpenRouter gateway — sets `HTTP-Referer` and `X-Title` headers |
|
|
465
|
+
| `OpenAICompatibleLLM` | Any OpenAI-compatible server (vLLM, MLX, LM Studio, Ollama `/v1`) |
|
|
466
|
+
|
|
457
467
|
**Switching providers is a one-line change:**
|
|
458
468
|
|
|
459
469
|
```python
|
|
@@ -585,6 +595,28 @@ recording.to_json("session.json")
|
|
|
585
595
|
|
|
586
596
|
replay_llm = LLM.replay(MEMORY.from_json("session.json"))
|
|
587
597
|
# replay_llm is a drop-in LLM: same flows, no network, identical outputs
|
|
598
|
+
|
|
599
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
600
|
+
# PROMPT CACHING — stable prefixes for agentic loops (v0.2.2+)
|
|
601
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
602
|
+
|
|
603
|
+
from thoughtflow import cached_system, cached_message
|
|
604
|
+
|
|
605
|
+
llm.call([
|
|
606
|
+
cached_system("You are a helpful assistant. Follow these rules exactly..."),
|
|
607
|
+
{"role": "user", "content": "Hello"},
|
|
608
|
+
])
|
|
609
|
+
# Anthropic: cache_control blocks on marked messages
|
|
610
|
+
# OpenAI-compat providers: markers stripped from bodies; opt-in wire params apply
|
|
611
|
+
|
|
612
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
613
|
+
# OBSERVABILITY — last_error, last_usage, request_timeout (v0.2.2+)
|
|
614
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
615
|
+
|
|
616
|
+
llm = LLM("openai:gpt-4o", key="sk-...", request_timeout=30)
|
|
617
|
+
response = llm.call([{"role": "user", "content": "Hi"}])
|
|
618
|
+
print(llm.last_usage) # normalized token counts when the provider returns them
|
|
619
|
+
print(llm.last_error) # None on success, error string on provider failure
|
|
588
620
|
```
|
|
589
621
|
|
|
590
622
|
**Key features:**
|
|
@@ -595,6 +627,9 @@ replay_llm = LLM.replay(MEMORY.from_json("session.json"))
|
|
|
595
627
|
- **Structured output** — JSON Schema enforcement via each provider's native mechanism
|
|
596
628
|
- **Streaming** — SSE/NDJSON streaming with a plain generator interface
|
|
597
629
|
- **Record/replay** — Capture exchanges into MEMORY; replay them deterministically
|
|
630
|
+
- **Prompt caching** — `cached_system()` / `cache=True` for stable agent prefixes
|
|
631
|
+
- **Production provider classes** — `GroqLLM`, `CerebrasLLM`, `OpenRouterLLM`
|
|
632
|
+
- **Unified param pipeline** — Transport keys (`base_url`, `referer`) never leak into JSON bodies
|
|
598
633
|
|
|
599
634
|
---
|
|
600
635
|
|
|
@@ -979,6 +1014,23 @@ memory = thought(memory)
|
|
|
979
1014
|
# Tokens stream through your hook in real time; the complete text still
|
|
980
1015
|
# flows through parsing/validation and lands in memory as usual.
|
|
981
1016
|
|
|
1017
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
1018
|
+
# PRODUCTION LANE — opt-in hardening (v0.2.2+, defaults unchanged)
|
|
1019
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
1020
|
+
|
|
1021
|
+
thought = THOUGHT(
|
|
1022
|
+
name="extract_plan",
|
|
1023
|
+
llm=llm,
|
|
1024
|
+
prompt="Return JSON for: {task}",
|
|
1025
|
+
parser="json",
|
|
1026
|
+
validation_mode="rescue", # try repair before failing
|
|
1027
|
+
strict_schema=True, # enforce schema shape strictly
|
|
1028
|
+
hold_msg=True, # keep failed assistant msg out of history
|
|
1029
|
+
on_failure=lambda m, err: m.add_log("extract_plan failed: " + str(err)),
|
|
1030
|
+
max_retries=2,
|
|
1031
|
+
)
|
|
1032
|
+
# Failures surface on MEMORY: {name}_last_error, {name}_attempts, failed: true events
|
|
1033
|
+
|
|
982
1034
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
983
1035
|
# SERIALIZATION — save and restore thoughts
|
|
984
1036
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -1015,6 +1067,8 @@ thought.last_response # Raw LLM response
|
|
|
1015
1067
|
- **Schema-based parsing** — Via `valid_extract` for bulletproof extraction
|
|
1016
1068
|
- **Multiple validators** — Built-in or custom, via the `validation=` parameter
|
|
1017
1069
|
- **Token streaming** — `on_token=` hook streams chunks without breaking the contract
|
|
1070
|
+
- **Production lane (opt-in)** — `validation_mode`, `strict_schema`, `hold_msg`, `on_failure`
|
|
1071
|
+
- **Structured failure visibility** — `{name}_last_error` and `{name}_attempts` on MEMORY
|
|
1018
1072
|
- **Four operations** — `llm_call`, `memory_query`, `variable_set`, `conditional`
|
|
1019
1073
|
- **Pre/post hooks** — Custom processing before and after execution
|
|
1020
1074
|
- **Full serialization** — Save, restore, and copy thoughts
|
|
@@ -1627,8 +1681,15 @@ vectors = embed.call(["Hello", "World"])
|
|
|
1627
1681
|
# Record/replay works here too — same seam as LLM
|
|
1628
1682
|
embed.record(memory)
|
|
1629
1683
|
replay_embed = EMBED.replay(memory)
|
|
1684
|
+
|
|
1685
|
+
# Gemini: text-embedding-004 aliases to gemini-embedding-001 automatically
|
|
1686
|
+
embed = EMBED("gemini:text-embedding-004", key="...")
|
|
1687
|
+
vector = embed.call("Hello world")
|
|
1688
|
+
print(embed.last_error) # None on success; error string on provider failure
|
|
1630
1689
|
```
|
|
1631
1690
|
|
|
1691
|
+
Supported embedding providers: OpenAI, Gemini, Groq, OpenRouter, and local Ollama (`ollama:all-minilm:33m`, etc.).
|
|
1692
|
+
|
|
1632
1693
|
---
|
|
1633
1694
|
|
|
1634
1695
|
## 🔧 Utilities
|
|
@@ -2236,6 +2297,18 @@ deterministic: no API keys in CI, no flaky outputs, no spend. The complete
|
|
|
2236
2297
|
lifecycle is demonstrated in
|
|
2237
2298
|
[`examples/scripts/13_record_replay.py`](examples/scripts/13_record_replay.py).
|
|
2238
2299
|
|
|
2300
|
+
**Integration smoke tests** (optional, real API calls) live under `tests/integration/`.
|
|
2301
|
+
Enable with `THOUGHTFLOW_INTEGRATION_TESTS=1` and provider API keys in your environment:
|
|
2302
|
+
|
|
2303
|
+
```bash
|
|
2304
|
+
THOUGHTFLOW_INTEGRATION_TESTS=1 pytest tests/integration/test_llm_providers.py \
|
|
2305
|
+
tests/integration/test_embed_providers.py -v
|
|
2306
|
+
```
|
|
2307
|
+
|
|
2308
|
+
These cover base `LLM` routing, native provider classes (`GroqLLM`, `CerebrasLLM`,
|
|
2309
|
+
`OpenRouterLLM`), Ollama, prompt caching, record/replay, and `EMBED` providers.
|
|
2310
|
+
Tests skip automatically when a key or account lacks access to a provider.
|
|
2311
|
+
|
|
2239
2312
|
---
|
|
2240
2313
|
|
|
2241
2314
|
## 📁 Project Structure
|
|
@@ -2245,6 +2318,9 @@ thoughtflow/
|
|
|
2245
2318
|
├── src/thoughtflow/
|
|
2246
2319
|
│ ├── __init__.py # Public API exports
|
|
2247
2320
|
│ ├── llm.py # LLM — multi-provider model interface
|
|
2321
|
+
│ ├── llm_params.py # Unified model-param pipeline (transport vs wire)
|
|
2322
|
+
│ ├── llm_cache.py # Prompt caching helpers (cached_system, etc.)
|
|
2323
|
+
│ ├── providers/ # GroqLLM, CerebrasLLM, OpenRouterLLM
|
|
2248
2324
|
│ ├── embed.py # EMBED — multi-provider embeddings
|
|
2249
2325
|
│ ├── memory.py # MEMORY — event-sourced state container
|
|
2250
2326
|
│ ├── thought.py # THOUGHT — atomic cognitive unit
|
|
@@ -2313,9 +2389,10 @@ See [developer/](developer/) for comprehensive development documentation.
|
|
|
2313
2389
|
| **Orchestration** | ✅ Stable | WORKFLOW, CHRON |
|
|
2314
2390
|
| **Action Subclasses** | ✅ Stable | 16 elemental operations (SEARCH, SCRAPE, FETCH, etc.) |
|
|
2315
2391
|
| **Record/Replay** | ✅ Shipped | LLM/EMBED recording, deterministic replay, eval harness |
|
|
2392
|
+
| **Provider convenience classes** | ✅ Shipped (0.2.2) | GroqLLM, CerebrasLLM, OpenRouterLLM, prompt caching |
|
|
2316
2393
|
| **API Stability** | 🟡 Alpha | May evolve based on feedback |
|
|
2317
2394
|
| **Documentation** | ✅ Per-primitive docs | `primitives/` folder with Markdown per class |
|
|
2318
|
-
| **Test Coverage** | ✅ ~
|
|
2395
|
+
| **Test Coverage** | ✅ ~744 unit tests | Plus optional integration smoke tests (API keys) |
|
|
2319
2396
|
| **Serverless Ready** | ✅ Yes | Zero deps, deployable example in `examples/serverless/` |
|
|
2320
2397
|
|
|
2321
2398
|
See [CHANGELOG.md](CHANGELOG.md) for version history.
|
|
@@ -93,7 +93,7 @@ supply-chain anxiety. The core library uses only Python's standard library —
|
|
|
93
93
|
pip install --upgrade thoughtflow
|
|
94
94
|
|
|
95
95
|
# Pin to a specific version for stability
|
|
96
|
-
pip install thoughtflow==0.2.
|
|
96
|
+
pip install thoughtflow==0.2.2
|
|
97
97
|
|
|
98
98
|
# Check your installed version
|
|
99
99
|
python -c "import thoughtflow; print(thoughtflow.__version__)"
|
|
@@ -381,8 +381,18 @@ ThoughtFlow works with **any LLM provider** through a unified interface:
|
|
|
381
381
|
| **Groq** | `groq:model` | `groq:llama-3.1-70b-versatile` | Fast inference for open models |
|
|
382
382
|
| **Google Gemini** | `gemini:model` | `gemini:gemini-1.5-pro` | Gemini Pro, Flash, etc. |
|
|
383
383
|
| **OpenRouter** | `openrouter:model` | `openrouter:anthropic/claude-3-opus` | Access any model via OpenRouter |
|
|
384
|
+
| **Cerebras** | `openai:model` + `OpenAICompatibleLLM` | `CerebrasLLM("gpt-oss-120b", key=...)` | OpenAI-compatible; Cloudflare-safe defaults |
|
|
384
385
|
| **Ollama** | `ollama:model` | `ollama:llama3.2` | Local models, no API key needed |
|
|
385
386
|
|
|
387
|
+
**Native convenience classes** (v0.2.2+) wrap production edge cases while keeping the same `call()` API:
|
|
388
|
+
|
|
389
|
+
| Class | When to use |
|
|
390
|
+
|-------|-------------|
|
|
391
|
+
| `GroqLLM` | Qwen models on Groq — sets `reasoning_format=hidden` by default |
|
|
392
|
+
| `CerebrasLLM` | Cerebras inference — safe `User-Agent`, optional auth-key fallback |
|
|
393
|
+
| `OpenRouterLLM` | OpenRouter gateway — sets `HTTP-Referer` and `X-Title` headers |
|
|
394
|
+
| `OpenAICompatibleLLM` | Any OpenAI-compatible server (vLLM, MLX, LM Studio, Ollama `/v1`) |
|
|
395
|
+
|
|
386
396
|
**Switching providers is a one-line change:**
|
|
387
397
|
|
|
388
398
|
```python
|
|
@@ -514,6 +524,28 @@ recording.to_json("session.json")
|
|
|
514
524
|
|
|
515
525
|
replay_llm = LLM.replay(MEMORY.from_json("session.json"))
|
|
516
526
|
# replay_llm is a drop-in LLM: same flows, no network, identical outputs
|
|
527
|
+
|
|
528
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
529
|
+
# PROMPT CACHING — stable prefixes for agentic loops (v0.2.2+)
|
|
530
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
531
|
+
|
|
532
|
+
from thoughtflow import cached_system, cached_message
|
|
533
|
+
|
|
534
|
+
llm.call([
|
|
535
|
+
cached_system("You are a helpful assistant. Follow these rules exactly..."),
|
|
536
|
+
{"role": "user", "content": "Hello"},
|
|
537
|
+
])
|
|
538
|
+
# Anthropic: cache_control blocks on marked messages
|
|
539
|
+
# OpenAI-compat providers: markers stripped from bodies; opt-in wire params apply
|
|
540
|
+
|
|
541
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
542
|
+
# OBSERVABILITY — last_error, last_usage, request_timeout (v0.2.2+)
|
|
543
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
544
|
+
|
|
545
|
+
llm = LLM("openai:gpt-4o", key="sk-...", request_timeout=30)
|
|
546
|
+
response = llm.call([{"role": "user", "content": "Hi"}])
|
|
547
|
+
print(llm.last_usage) # normalized token counts when the provider returns them
|
|
548
|
+
print(llm.last_error) # None on success, error string on provider failure
|
|
517
549
|
```
|
|
518
550
|
|
|
519
551
|
**Key features:**
|
|
@@ -524,6 +556,9 @@ replay_llm = LLM.replay(MEMORY.from_json("session.json"))
|
|
|
524
556
|
- **Structured output** — JSON Schema enforcement via each provider's native mechanism
|
|
525
557
|
- **Streaming** — SSE/NDJSON streaming with a plain generator interface
|
|
526
558
|
- **Record/replay** — Capture exchanges into MEMORY; replay them deterministically
|
|
559
|
+
- **Prompt caching** — `cached_system()` / `cache=True` for stable agent prefixes
|
|
560
|
+
- **Production provider classes** — `GroqLLM`, `CerebrasLLM`, `OpenRouterLLM`
|
|
561
|
+
- **Unified param pipeline** — Transport keys (`base_url`, `referer`) never leak into JSON bodies
|
|
527
562
|
|
|
528
563
|
---
|
|
529
564
|
|
|
@@ -908,6 +943,23 @@ memory = thought(memory)
|
|
|
908
943
|
# Tokens stream through your hook in real time; the complete text still
|
|
909
944
|
# flows through parsing/validation and lands in memory as usual.
|
|
910
945
|
|
|
946
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
947
|
+
# PRODUCTION LANE — opt-in hardening (v0.2.2+, defaults unchanged)
|
|
948
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
949
|
+
|
|
950
|
+
thought = THOUGHT(
|
|
951
|
+
name="extract_plan",
|
|
952
|
+
llm=llm,
|
|
953
|
+
prompt="Return JSON for: {task}",
|
|
954
|
+
parser="json",
|
|
955
|
+
validation_mode="rescue", # try repair before failing
|
|
956
|
+
strict_schema=True, # enforce schema shape strictly
|
|
957
|
+
hold_msg=True, # keep failed assistant msg out of history
|
|
958
|
+
on_failure=lambda m, err: m.add_log("extract_plan failed: " + str(err)),
|
|
959
|
+
max_retries=2,
|
|
960
|
+
)
|
|
961
|
+
# Failures surface on MEMORY: {name}_last_error, {name}_attempts, failed: true events
|
|
962
|
+
|
|
911
963
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
912
964
|
# SERIALIZATION — save and restore thoughts
|
|
913
965
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -944,6 +996,8 @@ thought.last_response # Raw LLM response
|
|
|
944
996
|
- **Schema-based parsing** — Via `valid_extract` for bulletproof extraction
|
|
945
997
|
- **Multiple validators** — Built-in or custom, via the `validation=` parameter
|
|
946
998
|
- **Token streaming** — `on_token=` hook streams chunks without breaking the contract
|
|
999
|
+
- **Production lane (opt-in)** — `validation_mode`, `strict_schema`, `hold_msg`, `on_failure`
|
|
1000
|
+
- **Structured failure visibility** — `{name}_last_error` and `{name}_attempts` on MEMORY
|
|
947
1001
|
- **Four operations** — `llm_call`, `memory_query`, `variable_set`, `conditional`
|
|
948
1002
|
- **Pre/post hooks** — Custom processing before and after execution
|
|
949
1003
|
- **Full serialization** — Save, restore, and copy thoughts
|
|
@@ -1556,8 +1610,15 @@ vectors = embed.call(["Hello", "World"])
|
|
|
1556
1610
|
# Record/replay works here too — same seam as LLM
|
|
1557
1611
|
embed.record(memory)
|
|
1558
1612
|
replay_embed = EMBED.replay(memory)
|
|
1613
|
+
|
|
1614
|
+
# Gemini: text-embedding-004 aliases to gemini-embedding-001 automatically
|
|
1615
|
+
embed = EMBED("gemini:text-embedding-004", key="...")
|
|
1616
|
+
vector = embed.call("Hello world")
|
|
1617
|
+
print(embed.last_error) # None on success; error string on provider failure
|
|
1559
1618
|
```
|
|
1560
1619
|
|
|
1620
|
+
Supported embedding providers: OpenAI, Gemini, Groq, OpenRouter, and local Ollama (`ollama:all-minilm:33m`, etc.).
|
|
1621
|
+
|
|
1561
1622
|
---
|
|
1562
1623
|
|
|
1563
1624
|
## 🔧 Utilities
|
|
@@ -2165,6 +2226,18 @@ deterministic: no API keys in CI, no flaky outputs, no spend. The complete
|
|
|
2165
2226
|
lifecycle is demonstrated in
|
|
2166
2227
|
[`examples/scripts/13_record_replay.py`](examples/scripts/13_record_replay.py).
|
|
2167
2228
|
|
|
2229
|
+
**Integration smoke tests** (optional, real API calls) live under `tests/integration/`.
|
|
2230
|
+
Enable with `THOUGHTFLOW_INTEGRATION_TESTS=1` and provider API keys in your environment:
|
|
2231
|
+
|
|
2232
|
+
```bash
|
|
2233
|
+
THOUGHTFLOW_INTEGRATION_TESTS=1 pytest tests/integration/test_llm_providers.py \
|
|
2234
|
+
tests/integration/test_embed_providers.py -v
|
|
2235
|
+
```
|
|
2236
|
+
|
|
2237
|
+
These cover base `LLM` routing, native provider classes (`GroqLLM`, `CerebrasLLM`,
|
|
2238
|
+
`OpenRouterLLM`), Ollama, prompt caching, record/replay, and `EMBED` providers.
|
|
2239
|
+
Tests skip automatically when a key or account lacks access to a provider.
|
|
2240
|
+
|
|
2168
2241
|
---
|
|
2169
2242
|
|
|
2170
2243
|
## 📁 Project Structure
|
|
@@ -2174,6 +2247,9 @@ thoughtflow/
|
|
|
2174
2247
|
├── src/thoughtflow/
|
|
2175
2248
|
│ ├── __init__.py # Public API exports
|
|
2176
2249
|
│ ├── llm.py # LLM — multi-provider model interface
|
|
2250
|
+
│ ├── llm_params.py # Unified model-param pipeline (transport vs wire)
|
|
2251
|
+
│ ├── llm_cache.py # Prompt caching helpers (cached_system, etc.)
|
|
2252
|
+
│ ├── providers/ # GroqLLM, CerebrasLLM, OpenRouterLLM
|
|
2177
2253
|
│ ├── embed.py # EMBED — multi-provider embeddings
|
|
2178
2254
|
│ ├── memory.py # MEMORY — event-sourced state container
|
|
2179
2255
|
│ ├── thought.py # THOUGHT — atomic cognitive unit
|
|
@@ -2242,9 +2318,10 @@ See [developer/](developer/) for comprehensive development documentation.
|
|
|
2242
2318
|
| **Orchestration** | ✅ Stable | WORKFLOW, CHRON |
|
|
2243
2319
|
| **Action Subclasses** | ✅ Stable | 16 elemental operations (SEARCH, SCRAPE, FETCH, etc.) |
|
|
2244
2320
|
| **Record/Replay** | ✅ Shipped | LLM/EMBED recording, deterministic replay, eval harness |
|
|
2321
|
+
| **Provider convenience classes** | ✅ Shipped (0.2.2) | GroqLLM, CerebrasLLM, OpenRouterLLM, prompt caching |
|
|
2245
2322
|
| **API Stability** | 🟡 Alpha | May evolve based on feedback |
|
|
2246
2323
|
| **Documentation** | ✅ Per-primitive docs | `primitives/` folder with Markdown per class |
|
|
2247
|
-
| **Test Coverage** | ✅ ~
|
|
2324
|
+
| **Test Coverage** | ✅ ~744 unit tests | Plus optional integration smoke tests (API keys) |
|
|
2248
2325
|
| **Serverless Ready** | ✅ Yes | Zero deps, deployable example in `examples/serverless/` |
|
|
2249
2326
|
|
|
2250
2327
|
See [CHANGELOG.md](CHANGELOG.md) for version history.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "thoughtflow"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Powerful AI systems from simple parts. Composable cognitive primitives, event-sourced memory, and autonomous agents in pure Python with zero dependencies."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -43,6 +43,8 @@ from __future__ import annotations
|
|
|
43
43
|
|
|
44
44
|
# Core primitives
|
|
45
45
|
from thoughtflow.llm import LLM, OpenAICompatibleLLM, ReplayLLM, ReplayMissError
|
|
46
|
+
from thoughtflow.llm_cache import cached_system, cached_message
|
|
47
|
+
from thoughtflow.providers import GroqLLM, CerebrasLLM, OpenRouterLLM
|
|
46
48
|
from thoughtflow.embed import EMBED, ReplayEMBED
|
|
47
49
|
from thoughtflow.memory import MEMORY
|
|
48
50
|
from thoughtflow.thought import THOUGHT
|
|
@@ -103,7 +105,7 @@ try:
|
|
|
103
105
|
|
|
104
106
|
__version__ = _get_version("thoughtflow")
|
|
105
107
|
except Exception:
|
|
106
|
-
__version__ = "0.
|
|
108
|
+
__version__ = "0.2.2"
|
|
107
109
|
|
|
108
110
|
# Public API
|
|
109
111
|
__all__ = [
|
|
@@ -112,6 +114,11 @@ __all__ = [
|
|
|
112
114
|
"OpenAICompatibleLLM",
|
|
113
115
|
"ReplayLLM",
|
|
114
116
|
"ReplayMissError",
|
|
117
|
+
"GroqLLM",
|
|
118
|
+
"CerebrasLLM",
|
|
119
|
+
"OpenRouterLLM",
|
|
120
|
+
"cached_system",
|
|
121
|
+
"cached_message",
|
|
115
122
|
"EMBED",
|
|
116
123
|
"ReplayEMBED",
|
|
117
124
|
"MEMORY",
|