thoughtflow 0.2.0__tar.gz → 0.2.3__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.
Files changed (87) hide show
  1. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/CHANGELOG.md +38 -5
  2. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/PKG-INFO +80 -3
  3. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/README.md +79 -2
  4. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/pyproject.toml +1 -1
  5. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/__init__.py +8 -1
  6. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/_util.py +264 -5
  7. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/embed.py +27 -5
  8. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/llm.py +188 -92
  9. thoughtflow-0.2.3/src/thoughtflow/llm_cache.py +211 -0
  10. thoughtflow-0.2.3/src/thoughtflow/llm_params.py +156 -0
  11. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/message.py +5 -6
  12. thoughtflow-0.2.3/src/thoughtflow/providers/README.md +15 -0
  13. thoughtflow-0.2.3/src/thoughtflow/providers/__init__.py +16 -0
  14. thoughtflow-0.2.3/src/thoughtflow/providers/cerebras.py +75 -0
  15. thoughtflow-0.2.3/src/thoughtflow/providers/groq.py +63 -0
  16. thoughtflow-0.2.3/src/thoughtflow/providers/openrouter.py +36 -0
  17. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/thought.py +368 -69
  18. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/thoughts/plan.py +69 -20
  19. thoughtflow-0.2.3/tests/integration/smoke_helpers.py +104 -0
  20. thoughtflow-0.2.3/tests/integration/test_embed_providers.py +123 -0
  21. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/integration/test_llm_providers.py +173 -2
  22. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_actions.py +4 -2
  23. thoughtflow-0.2.3/tests/unit/test_foundation_agent_response.py +240 -0
  24. thoughtflow-0.2.3/tests/unit/test_llm_provider_integration.py +288 -0
  25. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_replay.py +6 -6
  26. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_thought.py +1 -1
  27. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/.gitignore +0 -0
  28. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/LICENSE +0 -0
  29. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/_cron_expr.py +0 -0
  30. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/action.py +0 -0
  31. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/__init__.py +0 -0
  32. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/_http.py +0 -0
  33. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/_substitution.py +0 -0
  34. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/ask.py +0 -0
  35. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/call.py +0 -0
  36. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/fetch.py +0 -0
  37. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/noop.py +0 -0
  38. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/notify.py +0 -0
  39. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/post.py +0 -0
  40. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/read_file.py +0 -0
  41. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/run.py +0 -0
  42. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/say.py +0 -0
  43. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/scrape.py +0 -0
  44. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/search.py +0 -0
  45. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/sleep.py +0 -0
  46. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/wait.py +0 -0
  47. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/actions/write_file.py +0 -0
  48. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/agent.py +0 -0
  49. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/agents/__init__.py +0 -0
  50. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/agents/planact.py +0 -0
  51. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/agents/react.py +0 -0
  52. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/agents/reflect.py +0 -0
  53. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/chat.py +0 -0
  54. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/chron.py +0 -0
  55. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/delegate.py +0 -0
  56. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/eval/__init__.py +0 -0
  57. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/eval/harness.py +0 -0
  58. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/mcp.py +0 -0
  59. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/memory.py +0 -0
  60. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/py.typed +0 -0
  61. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/this.py +0 -0
  62. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/thoughts/__init__.py +0 -0
  63. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/thoughts/decide.py +0 -0
  64. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/tool.py +0 -0
  65. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/src/thoughtflow/workflow.py +0 -0
  66. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/__init__.py +0 -0
  67. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/conftest.py +0 -0
  68. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/integration/__init__.py +0 -0
  69. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/integration/test_chron.py +0 -0
  70. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/integration/test_scrape.py +0 -0
  71. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/integration/test_search_providers.py +0 -0
  72. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/__init__.py +0 -0
  73. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_action.py +0 -0
  74. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_agent.py +0 -0
  75. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_chat.py +0 -0
  76. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_chron.py +0 -0
  77. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_delegate.py +0 -0
  78. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_embed.py +0 -0
  79. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_eval.py +0 -0
  80. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_llm.py +0 -0
  81. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_mcp.py +0 -0
  82. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_memory.py +0 -0
  83. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_message.py +0 -0
  84. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_this.py +0 -0
  85. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_tool.py +0 -0
  86. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_util.py +0 -0
  87. {thoughtflow-0.2.0 → thoughtflow-0.2.3}/tests/unit/test_workflow.py +0 -0
@@ -5,16 +5,47 @@ 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
- ## [Unreleased]
8
+ ## [0.2.3] - 2026-07-11
9
+
10
+ ### Fixed
11
+ - Python 3.9 import failure: `TypeAlias` removed from `message.py` (stdlib support starts at 3.10)
12
+
13
+ ## [0.2.2] - 2026-07-10
14
+
15
+ Production-hardening release: THOUGHT/PLAN parsing improvements (Foundation Agent feedback) plus unified LLM param pipeline, prompt caching, and native provider convenience classes.
9
16
 
10
17
  ### Added
11
- - Nothing yet
18
+ - THOUGHT `on_failure=` hook and structured failure events on MEMORY (`{name}_last_error`, `{name}_attempts`)
19
+ - THOUGHT opt-in production lane: `validation_mode='rescue'`, `strict_schema=True`, `hold_msg=True`, `repair_target=`
20
+ - `valid_extract` optional `oneOf` schema dialect for branch-shaped responses
21
+ - PLAN `normalize_plan()` subclass hook and aggregate missing-param error messages
22
+ - LLM `last_error`, `last_usage`, opt-in `request_timeout=`, `on_usage=` callback
23
+ - Unified model-param pipeline (`split_call_params`) — transport keys never leak into JSON bodies
24
+ - Prompt caching: `cached_system()`, `cached_message()`, `cache=True` on messages; Anthropic `cache_control` wire adapter
25
+ - Native provider classes: `GroqLLM`, `CerebrasLLM`, `OpenRouterLLM`
26
+ - `docs/concepts/thought.md` — parse-validate pipeline guide
27
+ - Regression tests: `test_foundation_agent_response.py`, `test_llm_provider_integration.py`
28
+ - Example: `examples/scripts/15_thought_integration_test.py`
29
+ - Integration smoke tests: `tests/integration/test_llm_providers.py`, `test_embed_providers.py`
12
30
 
13
31
  ### Changed
14
- - Nothing yet
32
+ - Validation callables may return bare `bool` (coerced to tuple automatically)
33
+ - Parse-failure and repair-suffix messages are model-friendly
34
+ - `parser='json'` uses balanced JSON extraction
35
+ - Execution events include `failed: true` when retries exhausted with error
36
+ - OpenAI-compatible choice extraction falls back to `message.reasoning` when `content` is empty
37
+ - Expanded `docs/concepts/llm.md`, `primitives/LLM.md`, `primitives/THOUGHT.md`, `primitives/PLAN.md`
15
38
 
16
39
  ### Fixed
17
- - Nothing yet
40
+ - Bare-bool `validation=` lambdas no longer crash unpack
41
+ - PLAN param validation reports all missing params at once
42
+ - OpenRouter `referer`/`title` no longer leak into request JSON body
43
+ - EMBED Groq calls include User-Agent (Cloudflare HTTP 1010 without it)
44
+ - EMBED `text-embedding-004` aliases to `gemini-embedding-001`; `last_error` surfaced on failures
45
+
46
+ ## [Unreleased]
47
+
48
+ _Nothing yet._
18
49
 
19
50
  ---
20
51
 
@@ -171,7 +202,9 @@ grown from MEMORY itself, with zero new concepts.
171
202
  ---
172
203
 
173
204
  <!-- Release links -->
174
- [Unreleased]: https://github.com/jrolf/thoughtflow/compare/v0.2.0...HEAD
205
+ [Unreleased]: https://github.com/jrolf/thoughtflow/compare/v0.2.3...HEAD
206
+ [0.2.3]: https://github.com/jrolf/thoughtflow/compare/v0.2.2...v0.2.3
207
+ [0.2.2]: https://github.com/jrolf/thoughtflow/compare/v0.2.0...v0.2.2
175
208
  [0.2.0]: https://github.com/jrolf/thoughtflow/compare/v0.1.3...v0.2.0
176
209
  [0.1.3]: https://github.com/jrolf/thoughtflow/compare/v0.1.2...v0.1.3
177
210
  [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.0
3
+ Version: 0.2.3
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.0
167
+ pip install thoughtflow==0.2.3
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** | ✅ ~700 unit tests | Plus integration tests gated by API keys |
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.0
96
+ pip install thoughtflow==0.2.3
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** | ✅ ~700 unit tests | Plus integration tests gated by API keys |
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.0"
7
+ version = "0.2.3"
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.0.0"
108
+ __version__ = "0.2.3"
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",