1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! # LLM Pipeline
//!
//! Reusable node payloads for LLM workflows, with optional sequential chaining.
//!
//! This crate provides the building blocks for LLM-powered workflows:
//! **payloads** that execute LLM calls, **parsing utilities** for messy
//! model output, and a **chain** helper for sequential composition.
//!
//! Orchestration (routing, loops, concurrency, checkpoints) belongs in your
//! graph runtime (e.g. LangGraph). This crate provides what runs *inside*
//! each node.
//!
//! ## Core Concepts
//!
//! - **[`Payload`]** — object-safe trait for executable units. Takes a
//! `serde_json::Value` input, returns a [`PayloadOutput`].
//! - **[`ExecCtx`]** — shared execution context (HTTP client, endpoint,
//! template vars, cancellation, optional event handler).
//! - **[`LlmCall`]** — the primary payload: renders prompts, calls Ollama,
//! parses responses.
//! - **[`Chain`]** — sequential composition of payloads.
//! - **[`PayloadOutput`]** — `Value`-based output with `parse_as::<T>()` for
//! typed extraction at workflow edges.
//!
//! ## Quick Start (Payload API)
//!
//! ```no_run
//! use llm_pipeline::{LlmCall, Chain, ExecCtx};
//! use llm_pipeline::payload::Payload;
//! use serde::Deserialize;
//! use serde_json::json;
//!
//! #[derive(Debug, Deserialize)]
//! struct Analysis { summary: String }
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let ctx = ExecCtx::builder("http://localhost:11434").build();
//!
//! let chain = Chain::new("analyze")
//! .push(Box::new(
//! LlmCall::new("draft", "Analyze: {input}")
//! .with_config(llm_pipeline::LlmConfig::default().with_json_mode(true))
//! ))
//! .push(Box::new(
//! LlmCall::new("refine", "Refine this analysis: {input}")
//! .with_config(llm_pipeline::LlmConfig::default().with_json_mode(true))
//! ));
//!
//! let output = chain.execute(&ctx, json!("Your text here")).await?;
//! let result: Analysis = output.parse_as()?;
//! println!("{}", result.summary);
//! Ok(())
//! }
//! ```
//!
//! ## Pipeline API (compatibility)
//!
//! The original `Pipeline<T>` API is still available and works as before.
//! Internally it now uses [`LlmCall`] payloads.
//!
//! ```no_run
//! use llm_pipeline::{Pipeline, Stage, PipelineInput};
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Debug, Clone, Serialize, Deserialize)]
//! struct Analysis { summary: String, insights: Vec<String> }
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let client = reqwest::Client::new();
//! let pipeline = Pipeline::<Analysis>::builder()
//! .add_stage(Stage::new("analyze", "Analyze: {input}").with_json_mode(true))
//! .add_stage(Stage::new("refine", "Refine: {input}").with_json_mode(true))
//! .build()?;
//!
//! let result = pipeline.execute(
//! &client, "http://localhost:11434", PipelineInput::new("Your text"),
//! ).await?;
//! println!("{}", result.final_output.summary);
//! Ok(())
//! }
//! ```
// --- New payload layer ---
// --- Original modules (still public) ---
// --- Primary exports: new payload API ---
pub use OpenAiBackend;
pub use ;
pub use Chain;
pub use ParseDiagnostics;
pub use ;
pub use PipelineLimits;
pub use LlmCall;
pub use OutputStrategy;
pub use ;
pub use RetryConfig;
pub use ;
pub use StreamingDecoder;
pub use ;
pub use TraceId;
// --- Canonical cross-crate trace/identity re-exports from stack-ids ---
pub use TraceCtx;
// --- Re-exports: original API (compatibility) ---
pub use LlmConfig;
pub use ;
pub use ;
pub use ;
pub use ;