core
Core FFAI infrastructure: client abstraction, prompt assembly, history, and export.
Core FFAI infrastructure: client abstraction, prompt assembly, history, and export.
- class AsyncFFAIClientBase[source]
Bases:
FFAIClientBaseAsync variant of
FFAIClientBasefor use withexecute_graph.Subclasses must implement
generate_responseandcloneas async methods. All other methods (clear_conversation,get_conversation_history,set_conversation_history) remain synchronous because they operate on in-memory lists.- abstractmethod async generate_response(prompt, **kwargs)[source]
Generate a response from the AI model.
- class AsyncGraphExecutor(executor_fn, max_concurrency=10, prompt_resolver=None)[source]
Bases:
objectExecute a prompt DAG with topological-parallel async calls.
- Parameters:
executor_fn (Callable[..., Awaitable[ResponseResult]]) – Async callable that takes prompt kwargs and returns a
ResponseResult.max_concurrency (int) – Maximum number of concurrent API calls, enforced by an
asyncio.Semaphore.prompt_resolver (Callable[[dict[str, Any], dict[str, dict[str, Any]]], tuple[str, set[str]]] | None) – Optional callback
(prompt_spec, results_by_name) -> (resolved_prompt, interpolated_names). When provided, each node’s prompt is resolved before execution. WhenNone, prompts are sent as-is.
- async execute(prompts)[source]
Build and execute a prompt dependency graph.
- Parameters:
prompts (list[dict[str, Any]]) – List of prompt dicts with keys: prompt_name, prompt, history, condition, abort_condition.
- Returns:
GraphResultwith per-prompt results and aggregate counts.- Raises:
ValueError – If a dependency cycle is detected.
- Return type:
- class ConditionEvaluator(results_by_name)[source]
Bases:
objectSafely evaluates condition expressions for conditional prompt execution.
Security model: Never uses eval() or exec() on user input. Conditions are parsed using AST and evaluated using a restricted set of operators, functions, and method calls.
- Syntax:
{{prompt_name.property}} == “value” {{prompt_name.property}} != “value” {{prompt_name.property}} contains “substring” {{prompt_name.property}} not contains “substring” {{prompt_name.property}} matches “regex” len({{prompt_name.response}}) > 100 {{a.status}} == “success” and {{b.status}} == “success” {{a.status}} == “success” or {{b.status}} == “success” not {{prompt_name.has_response}}
- String methods:
{{prompt_name.response}}.startswith(“prefix”) {{prompt_name.response}}.endswith(“suffix”) {{prompt_name.response}}.lower() == “value” {{prompt_name.response}}.split(“,”)[0]
- JSON functions:
json_get({{prompt_name.response}}, “key.nested[0]”) json_get_default({{prompt_name.response}}, “key”, “default”) json_has({{prompt_name.response}}, “key”) json_keys({{prompt_name.response}}) “key” in json_keys({{prompt_name.response}})
- Available properties:
status: “success”, “failed”, “skipped”
response: The AI response text
attempts: Number of retry attempts (int)
error: Error message if failed (str)
has_response: True if response exists and non-empty (bool)
Note
For JSON responses, use
json_has(),json_get(), or"key" in json_keys(...)to query structure. Thecontainsandinoperators always perform substring matching on the stringified response text, which can produce false matches against dict/list repr strings (e.g."True" in {{s.response}}matches"{'pass': True}"because the four characters appear in the repr, not because of a value).- ALLOWED_OPERATORS = {<class 'ast.And'>: <function ConditionEvaluator.<lambda>>, <class 'ast.Eq'>: <built-in function eq>, <class 'ast.Gt'>: <built-in function gt>, <class 'ast.GtE'>: <built-in function ge>, <class 'ast.Lt'>: <built-in function lt>, <class 'ast.LtE'>: <built-in function le>, <class 'ast.NotEq'>: <built-in function ne>, <class 'ast.Or'>: <function ConditionEvaluator.<lambda>>}
- ALLOWED_UNARY_OPERATORS = {<class 'ast.Not'>: <built-in function not_>}
- ALLOWED_FUNCTIONS = {'abs': <built-in function abs>, 'bool': <function ConditionEvaluator.<lambda>>, 'count': <function ConditionEvaluator.<lambda>>, 'find': <function ConditionEvaluator.<lambda>>, 'float': <function ConditionEvaluator.<lambda>>, 'int': <function ConditionEvaluator.<lambda>>, 'is_empty': <function ConditionEvaluator.<lambda>>, 'is_null': <function ConditionEvaluator.<lambda>>, 'json_get': <function ConditionEvaluator.<lambda>>, 'json_get_default': <function ConditionEvaluator.<lambda>>, 'json_has': <function ConditionEvaluator.<lambda>>, 'json_keys': <function ConditionEvaluator.<lambda>>, 'json_parse': <function ConditionEvaluator.<lambda>>, 'json_type': <function ConditionEvaluator.<lambda>>, 'json_values': <function ConditionEvaluator.<lambda>>, 'len': <built-in function len>, 'lower': <function ConditionEvaluator.<lambda>>, 'lstrip': <function ConditionEvaluator.<lambda>>, 'max': <built-in function max>, 'min': <built-in function min>, 'replace': <function ConditionEvaluator.<lambda>>, 'rfind': <function ConditionEvaluator.<lambda>>, 'round': <built-in function round>, 'rsplit': <function ConditionEvaluator.<lambda>>, 'rstrip': <function ConditionEvaluator.<lambda>>, 'slice': <function ConditionEvaluator.<lambda>>, 'split': <function ConditionEvaluator.<lambda>>, 'str': <function ConditionEvaluator.<lambda>>, 'strip': <function ConditionEvaluator.<lambda>>, 'trim': <function ConditionEvaluator.<lambda>>, 'upper': <function ConditionEvaluator.<lambda>>}
- ALLOWED_STRING_METHODS = frozenset({'capitalize', 'center', 'count', 'endswith', 'find', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'islower', 'isnumeric', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'replace', 'rfind', 'rindex', 'rjust', 'rsplit', 'rstrip', 'split', 'startswith', 'strip', 'title', 'upper', 'zfill'})
- ALLOWED_LIST_METHODS = frozenset({'count', 'index'})
- ALLOWED_DICT_METHODS = frozenset({'get', 'keys', 'values'})
- VARIABLE_PATTERN = re.compile('\\{\\{(\\w+)\\.(\\w+)\\}\\}')
- evaluate_with_trace(condition)[source]
Evaluate a condition expression and return the resolved trace.
Like evaluate(), but also returns the resolved expression showing what each {{name.property}} was substituted with.
- Parameters:
condition (str) – The condition string to evaluate.
- Returns:
Tuple of (result, error_message, resolved_trace). - result: True if condition passes, False otherwise. - error_message: None if successful, error string if failed. - resolved_trace: The condition after variable substitution,
e.g. ‘“failed” == “success”’, or None if no condition.
- Return type:
- class ConversationHistory[source]
Bases:
objectAPI-facing message history for provider SDK calls.
Unlike
PermanentHistory, turns here carry no timestamps and the history is intended to be cleared (e.g. when starting a new conversation context). Theget_turns()output is structured for direct injection into provider message APIs.- add_turn_assistant(content)[source]
Append an assistant turn to the history.
- Parameters:
content (str) – The assistant’s response text.
- Return type:
None
- class DependencyEdge(from_seq, to_seq, source, condition_text=None)[source]
Bases:
objectA single dependency edge in the execution graph.
- class Embeddings(model='mistral/mistral-embed', api_key=None, api_base=None, cache_enabled=True, cache_size=256, device='cpu', **kwargs)[source]
Bases:
objectCompute text embeddings using local or remote models.
Supports local sentence-transformer models (prefix
local/) and remote providers via LiteLLM (e.g.mistral/mistral-embed,openai/text-embedding-3-small). Results are optionally cached in an LRU-style in-memory cache.- Parameters:
model (str) – Model identifier. Use
local/<name>for local sentence-transformer models, or<provider>/<model>for remote APIs.api_key (str | None) – API key for the remote provider. If None, the key is read from a provider-specific environment variable.
api_base (str | None) – Optional custom API base URL.
cache_enabled (bool) – Whether to cache embedding results in memory.
cache_size (int) – Maximum number of entries in the embedding cache.
device (str) – Torch device for local models (e.g.
"cpu","cuda").**kwargs (Any) – Extra keyword arguments forwarded to the LiteLLM embedding call.
- clear_cache()[source]
Clear the embedding cache.
- Returns:
Number of entries that were in the cache before clearing.
- Return type:
- class ExecutionGraph(nodes=<factory>, edges=<factory>, max_level=0)[source]
Bases:
objectComplete execution graph with dependency metadata.
- Parameters:
nodes (dict[int, PromptNode])
edges (list[DependencyEdge])
max_level (int)
- nodes
Dictionary mapping sequence numbers to PromptNodes.
- Type:
- edges
List of all dependency edges with source information.
- nodes: dict[int, PromptNode]
- edges: list[DependencyEdge]
- class ExecutionState(completed=<factory>, in_progress=<factory>, pending=<factory>, results=<factory>, results_lock=<factory>, success_count=0, failed_count=0, skipped_count=0, aborted_count=0, results_by_name=<factory>, current_name='', aborted=False)[source]
Bases:
objectTracks state during parallel execution with thread-safe operations.
- Parameters:
- pending
Dict of pending PromptNodes by sequence number.
- Type:
- results_lock
Thread lock for result access.
- Type:
_thread.allocate_lock
- pending: dict[int, PromptNode]
- results_lock: allocate_lock
- class FFAIClientBase[source]
Bases:
ABCAbstract base class defining the contract for AI client implementations.
All AI provider clients (Mistral, Anthropic, OpenAI, etc.) must inherit from this class and implement its abstract methods.
- retry_config
Override retry configuration.
Noneuses the global config defaults.
- property last_usage: TokenUsage | None
Token usage from the most recent generate_response() call.
- property last_duration_ms: float | None
Wall-clock duration in ms of the most recent generate_response() call.
- add_tool_result(tool_call_id, content)[source]
Add a tool result to the conversation history.
Default implementation appends a tool-role message. Subclasses that manage conversation history differently (e.g. via an external API) should override this method.
- class GraphResult(results=<factory>, success_count=0, failed_count=0, skipped_count=0, aborted=False, aborted_count=0)[source]
Bases:
objectAggregate result from executing a prompt dependency graph.
- Parameters:
- results
Mapping of prompt_name to
ResponseResult.
- results: dict[str, ResponseResult]
- class HistoryExporter(history, clean_history, prompt_attr_history, ordered_history, persist_dir, persist_name=None, auto_persist=False)[source]
Bases:
objectDataFrame export and persistence for FFAI interaction histories.
Converts the various history stores into Polars DataFrames and handles persistence to Parquet files.
- Parameters:
history (list[dict[str, Any]]) – Raw interaction history list.
clean_history (list[dict[str, Any]]) – Cleaned interaction history list.
prompt_attr_history (list[dict[str, Any]]) – Prompt-attribute-indexed history list.
ordered_history (Any) – OrderedPromptHistory instance.
persist_dir (str) – Directory for persisted files.
persist_name (str | None) – Base name for persisted files.
auto_persist (bool) – Whether to auto-persist DataFrames on creation.
- history_to_dataframe()[source]
Convert the full interaction history to a polars DataFrame.
- Return type:
DataFrame
- clean_history_to_dataframe()[source]
Convert the clean interaction history to a polars DataFrame.
- Return type:
DataFrame
- prompt_attr_history_to_dataframe()[source]
Convert the prompt attribute history to a polars DataFrame.
- Return type:
DataFrame
- ordered_history_to_dataframe()[source]
Convert the ordered interaction history to a polars DataFrame.
- Return type:
DataFrame
- search_history(text=None, prompt_name=None, model=None, start_time=None, end_time=None)[source]
Search interaction history with flexible filtering options.
- Parameters:
text (str | None) – Text to search for in prompts and responses
prompt_name (str | None) – Filter by prompt name
model (str | None) – Filter by model name
start_time (float | None) – Filter by timestamp (start time in epoch seconds)
end_time (float | None) – Filter by timestamp (end time in epoch seconds)
- Returns:
Filtered dataframe of interactions
- Return type:
pl.DataFrame
- get_prompt_name_stats_df(prompt_name_stats)[source]
Get statistics on prompt name usage as a DataFrame.
- get_response_length_stats()[source]
Get statistics on response lengths by prompt name.
- Returns:
DataFrame with response length statistics by prompt name
- Return type:
pl.DataFrame
- class Memory(embeddings, store=None)[source]
Bases:
objectSemantic recall over completed conversation turns.
Wraps an
EmbeddingBackendand aTurnVectorStore. Provides two indexing entry points:index_turn()/aindex_turn()— extract text from a structuredturndict viaturn["content"][0]["text"].index_turn_text()/aindex_turn_text()— embed an arbitrary caller-supplied string while still storing the structuredturndict alongside. Used byHistoryRecorder(L4) to embed the Q+A pair (f"{prompt}\n{response}") rather than just the response.
- Parameters:
embeddings (EmbeddingBackend) – Embedding backend (LiteLLM API model or local
local/...model). Any object implementingEmbeddingBackendis accepted.store (TurnVectorStore | None) – Optional
TurnVectorStore. Defaults to a fresh instance. Public and settable so callers can swap in a store loaded from Parquet (L3).
- index_turn_text(text, turn, metadata=None)[source]
Embed an arbitrary
textand store the structuredturn.Use this when the embedded text differs from
turn["content"][0]["text"]— e.g., when embedding the Q+A pair (f"{prompt}\n{response}") while still storing the response as the canonical turn content.
- async aindex_turn(turn, metadata=None)[source]
Async variant of
index_turn().
- async aindex_turn_text(text, turn, metadata=None)[source]
Async variant of
index_turn_text().
- reindex(new_embeddings)[source]
Re-embed all stored texts with a new embedding model.
Reads all entries via
TurnVectorStore.iter_entries(), clears the store, and re-adds each entry with the new embedding. The turn dicts and metadata are preserved verbatim. After reindexing,self._embeddingsis updated so subsequentsearch()calls embed queries with the new model.- Parameters:
new_embeddings (EmbeddingBackend) – The new embedding backend to use.
- Return type:
None
- class OrderedPromptHistory[source]
Bases:
objectManages ordered prompt-response history with named references.
This class provides: - Named prompt storage for declarative context assembly - Sequential ordering of interactions - Query capabilities by prompt name, model, etc. - History chain tracking for dependency resolution
- prompt_dict
OrderedDict mapping prompt names to interaction lists.
- prompt_dict: OrderedDict[str, list[Interaction]]
- get_effective_prompt_name(prompt_name)[source]
Get the effective prompt name from various input types.
- add_interaction(model, prompt, response, prompt_name=None, history=None)[source]
Add a new interaction to the history, storing cleaned versions of prompt and response.
- Parameters:
model (str) – The model used for the interaction
prompt (str) – The prompt text
response (str) – The response text
prompt_name (str | None) – Optional name/key for the prompt. If None, uses prompt text as key
history (list[str] | None) – Optional list of prompt names that form the history chain for this interaction
- Returns:
The created Interaction object
- Return type:
- get_interactions_by_prompt_name(prompt_name)[source]
Get all interactions for a specific prompt name.
- Parameters:
prompt_name (str)
- Return type:
- get_latest_interaction_by_prompt_name(prompt_name)[source]
Get the most recent interaction for a specific prompt name.
- Parameters:
prompt_name (str)
- Return type:
Interaction | None
- get_interactions_by_model_and_prompt_name(model, prompt_name)[source]
Get all interactions for a specific model and prompt name combination.
- Parameters:
- Return type:
- merge_histories(other)[source]
Merge another OrderedPromptHistory into this one.
- Parameters:
other (OrderedPromptHistory) – Another OrderedPromptHistory instance to merge
- Return type:
None
- get_interaction_by_prompt(prompt)[source]
Get an interaction by its exact prompt text.
Useful when prompt was used as the prompt_name.
- Parameters:
prompt (str)
- Return type:
Interaction | None
- class PermanentHistory[source]
Bases:
objectAppend-only chronological turn history with timestamps.
Each turn stores a
role("user"or"assistant"), structuredcontent, a per-turntimestamp, and optionalmetadata. Consecutive user turns are coalesced by appending content unless the caller passes non-Nonemetadata, in which case a new turn is always created (so distinctprompt_namemetadata is never silently merged).- add_turn_assistant(content, metadata=None)[source]
Append an assistant turn with the current timestamp.
- add_turn_user(content, metadata=None)[source]
Append a user turn, coalescing with the previous user turn when adjacent.
Coalescing only happens when metadata is
None. If metadata is provided, a new turn is always created so distinctprompt_namemetadata is never silently merged into the previous user turn.
- class PromptBuilder(prompt_attr_history)[source]
Bases:
objectBuilds final prompts with history context and variable interpolation.
Reads from the shared prompt-attribute history to resolve
{{prompt_name.response}}patterns and assemble conversation context blocks.- build_prompt(prompt, history=None, dependencies=None, strict=False)[source]
Build the final prompt with history and variable interpolation.
- Parameters:
prompt (str) – The prompt template (may contain {{}} interpolation patterns)
history (list[str] | None) – List of prompt names to include in conversation history
dependencies (Any | None) – Additional dependencies (unused but kept for compatibility)
strict (bool) – If True, raise ValueError on unknown {{name.response}} references
- Returns:
Tuple of (final_prompt, set_of_interpolated_prompt_names)
- Return type:
- class PromptNode(sequence, prompt, dependencies=<factory>, level=0)[source]
Bases:
objectRepresents a prompt in the execution dependency graph.
- class ResponseContext(shared_prompt_attr_history=None, history_lock=None)[source]
Bases:
objectManages the shared
prompt_attr_historylist with thread-safe recording.This class centralises all mutation of the shared prompt-attribute history that
PromptBuilderreads from for{{name.response}}interpolation. It replaces the inline history recording previously scattered throughFFAI.generate_response()and direct list appends inPlanningRunner/SynthesisRunner.- Parameters:
shared_prompt_attr_history (list[dict[str, Any]] | None) – Optional pre-existing list to share. When
Nonea new empty list is created.history_lock (threading.Lock | None) – Optional lock for thread-safe access.
- record(prompt, response, model, prompt_name=None, history=None)[source]
Record an interaction to
prompt_attr_history.If response is a
dict(JSON response from the LLM), each top-level key-value pair is recorded as a separate entry so that{{key_name.response}}interpolation works.- Parameters:
prompt (str) – The original prompt text (or JSON key name).
response (Any) – The cleaned response (str, dict, etc.).
model (str) – Model identifier used for this call.
prompt_name (str | None) – Optional logical name for the prompt.
history (list[str] | None) – Optional list of history dependency names.
- Return type:
None
- record_raw(interaction)[source]
Append a pre-built interaction dict.
Used by
PlanningRunnerandSynthesisRunnerto inject results that were not produced byFFAI.generate_response().
- property history_lock: threading.Lock | None
The lock used for thread-safe history access.
- class ResponseResult(response, resolved_prompt='', usage=None, cost_usd=0.0, model='', duration_ms=0.0, status='success', condition_trace=None, condition_error=None, parsed=None, parsing_errors=None)[source]
Bases:
objectStructured result from an FFAI response generation call.
Replaces side-channel attributes (
last_usage,last_cost_usd,last_resolved_prompt) with a single typed return value.- Parameters:
- response
The cleaned AI response (str, dict, list, etc.).
- Type:
Any
- usage
Token usage from the API call, if available.
- Type:
ffai.core.usage.TokenUsage | None
- parsed
Validated Pydantic model instance (when response_model is used).
- Type:
Any
- parsing_errors
Validation error strings if structured output parsing failed.
- usage: TokenUsage | None = None
- class StructuredOutputHandler(max_retries=2)[source]
Bases:
objectValidates LLM responses against Pydantic models.
- Parameters:
max_retries (int) – Maximum number of re-prompt attempts after validation failure. Total attempts = max_retries + 1.
- build_response_format(model)[source]
Return the Pydantic model class for use as
response_format.LiteLLM’s
completion()acceptstype[BaseModel]directly in theresponse_formatparameter and handles provider-specific schema translation internally. Returning the model class instead of a hand-built dict avoids incompatible schemas across providers.
- build_system_suffix(model)[source]
Generate instruction text describing the expected JSON schema.
Intended to be appended to system instructions so the model knows the exact output shape.
- validate(response, model)[source]
Parse and validate an LLM response against a Pydantic model.
Uses
json_repairfor fault-tolerant JSON parsing, then Pydantic validation for type safety.- Parameters:
- Returns:
StructuredResultwithparsedset on success, orparsing_errorspopulated on failure.- Return type:
- prepare_retry_state(prompt, response_model)[source]
Initialize retry state for a structured output loop.
- process_attempt(response, response_model, prompt, attempt, all_errors, best_result)[source]
Process a single structured output attempt.
- Parameters:
response (str) – The raw LLM response.
response_model (type[BaseModel]) – Pydantic BaseModel subclass.
prompt (str) – The original resolved prompt (for feedback).
attempt (int) – Zero-indexed attempt number.
best_result (StructuredResult | None) – Best result so far.
- Returns:
Tuple of (updated_best_result, next_prompt, updated_errors, should_stop).
- Return type:
- finalize_retry(best_result, all_errors, max_attempts)[source]
Finalize after all retry attempts exhausted.
- Parameters:
best_result (StructuredResult | None) – Last validation result.
max_attempts (int) – Total attempts made.
- Returns:
Final
StructuredResult.- Return type:
- class StructuredResult(parsed, raw_response, attempts, parsing_errors=<factory>)[source]
Bases:
objectOutcome of a structured-output validation attempt.
- parsed
Validated Pydantic model instance, or None on failure.
- Type:
pydantic.main.BaseModel | None
- class TurnHit(score, turn, turn_index, text, metadata)[source]
Bases:
objectA single ranked result returned by
TurnVectorStore.search.- Parameters:
- score
Cosine similarity in
[-1.0, 1.0]between the query embedding and the stored turn’s embedding. Sorted descending bysearch().- Type:
- turn
The raw turn dict stored at index time. Mirrors the
PermanentHistoryturn shape:{"role": str, "content": [{"type": "text", "text": str}], "timestamp": float, "metadata": dict}.
- turn_index
Position of the entry in
TurnVectorStoreat the time of the search. Stable within a singlesearch()call for a given store; may shift afterclear().- Type:
- text
Pre-extracted plain text (typically the Q+A pair) that was embedded. Equal to the string passed to
add(text=...).- Type:
- metadata
Caller-provided metadata attached at index time. Always present (possibly empty dict). Tier 2 will populate
user_id/session_id/agent_idhere.
- class TurnVectorStore[source]
Bases:
objectAppend-only in-memory store of embedded turns with cosine search.
Entries are stored in four parallel lists keyed by an integer index.
add()returns the index of the appended entry; that index is preserved on the correspondingTurnHitreturned bysearch().Search is O(N) over all stored entries. Adequate for Tier 1 scale; a vector-index backend (Chroma/Qdrant) is a future concern.
- add(text, embedding, turn, metadata=None)[source]
Append an entry and return its integer index.
- Parameters:
- Returns:
The integer index of the newly appended entry.
- Return type:
- search(query_embedding, top_k=5, threshold=None)[source]
Return up to
top_kturns ranked by cosine similarity.- Parameters:
- Returns:
Hits sorted by
scoredescending. Empty list if the store is empty or all hits fall belowthreshold.- Return type:
- build_execution_graph(prompts)[source]
Build dependency graph for parallel execution.
Delegates to build_execution_graph_with_edges and returns only the nodes dict for backward compatibility.
- Parameters:
prompts (Sequence[dict[str, Any]]) – List of prompt dictionaries with sequence, prompt_name, history, and condition fields.
- Returns:
Dictionary mapping sequence numbers to PromptNodes.
- Raises:
ValueError – If a dependency cycle is detected.
- Return type:
- build_execution_graph_with_edges(prompts)[source]
Build execution graph with full edge source metadata.
- Parameters:
prompts (Sequence[dict[str, Any]]) – List of prompt dictionaries.
- Returns:
ExecutionGraph with nodes, edges, and max_level.
- Raises:
ValueError – If a dependency cycle is detected.
- Return type:
- build_graph_history_dict(results_by_name)[source]
Build a
{name: response_text}mapping from executor results.
- check_abort_condition(prompt_spec, results_by_name)[source]
Check if a prompt’s
abort_conditiontriggers a cascade abort.
- clean_response(response)[source]
Process and validate a response, removing think tags and extracting JSON.
- evaluate_condition(prompt, results_by_name, condition_field='condition')[source]
Evaluate a prompt’s condition.
- Parameters:
- Returns:
Tuple of (should_execute, condition_result, condition_error).
- Return type:
- evaluate_condition_with_trace(prompt, results_by_name, condition_field='condition')[source]
Evaluate a prompt’s condition and return the resolved trace.
- Parameters:
- Returns:
Tuple of (should_execute, condition_result, condition_error, condition_trace).
- Return type:
- extract_json(text)[source]
Extract JSON from text using fault-tolerant
json_repair.Handles markdown code blocks, trailing commas, unquoted keys, and other common LLM JSON mistakes.
- extract_json_field(data, path)[source]
Extract a value from JSON using dot notation path.
Supports: - Simple fields: “field_name” - Nested objects: “object.field” - Array indices: “array.0” - Combined: “object.array.0.field”
- get_ready_prompts(state, nodes)[source]
Get prompts ready for execution (all dependencies completed).
- Parameters:
state (ExecutionState) – Current execution state.
nodes (dict[int, PromptNode]) – Execution graph nodes.
- Returns:
List of PromptNodes ready for execution, sorted by level and sequence.
- Return type:
- interpolate_prompt(prompt, history, strict=False)[source]
Replace {{prompt_name.response}} patterns with actual content.
- Parameters:
- Returns:
Tuple of (resolved_prompt, set_of_interpolated_prompt_names)
- Return type:
- resolve_graph_prompt(prompt_spec, results_by_name)[source]
Resolve interpolation and history injection for a graph node.
Performs two-phase prompt assembly:
Interpolation: Resolves
{{name.response}}patterns using results from earlier DAG levels.History injection: For each name in
prompt_spec["history"], formats the prior Q&A as<conversation_history>XML. Entries already interpolated are deduplicated.
- run_sync(coro)[source]
Run an async coroutine from synchronous code.
If no event loop is running, delegates to
asyncio.run(). If an event loop is already running (e.g. inside a Jupyter notebook or an existing async framework), runs the coroutine in a background thread to avoidRuntimeError.
- should_skip_for_failed_deps(node, results_by_name, nodes)[source]
Check if a node should be skipped because any dependency failed.
Only failed dependencies trigger a skip. Skipped dependencies do NOT cascade — a node whose only dependency was skipped will still execute (its condition will determine whether it should proceed).
- Parameters:
node (PromptNode) – The prompt node to check.
results_by_name (dict[str, dict[str, Any]]) – Accumulated results indexed by prompt_name.
nodes (dict[int, PromptNode]) – All graph nodes indexed by sequence number.
- Returns:
Tuple of (should_skip, reason_string).
- Return type:
Classes
- class AsyncFFAIClientBase[source]
Bases:
FFAIClientBaseAsync variant of
FFAIClientBasefor use withexecute_graph.Subclasses must implement
generate_responseandcloneas async methods. All other methods (clear_conversation,get_conversation_history,set_conversation_history) remain synchronous because they operate on in-memory lists.- abstractmethod async generate_response(prompt, **kwargs)[source]
Generate a response from the AI model.
- abstractmethod async clone()[source]
Create a fresh async clone of this client with empty history.
- Returns:
New
AsyncFFAIClientBaseinstance with same config, empty history.- Return type:
- add_tool_result(tool_call_id, content)
Add a tool result to the conversation history.
Default implementation appends a tool-role message. Subclasses that manage conversation history differently (e.g. via an external API) should override this method.
- abstractmethod clear_conversation()
Clear the conversation history.
- Return type:
None
- configure_retry(retry_config=None)
Configure retry behavior for this client.
- abstractmethod get_conversation_history()
Get the conversation history.
- static get_default_retry_config()
Get default retry configuration from global config.
- property last_duration_ms: float | None
Wall-clock duration in ms of the most recent generate_response() call.
- property last_usage: TokenUsage | None
Token usage from the most recent generate_response() call.
- abstractmethod set_conversation_history(history)
Set the conversation history.
- class AsyncGraphExecutor(executor_fn, max_concurrency=10, prompt_resolver=None)[source]
Bases:
objectExecute a prompt DAG with topological-parallel async calls.
- Parameters:
executor_fn (Callable[..., Awaitable[ResponseResult]]) – Async callable that takes prompt kwargs and returns a
ResponseResult.max_concurrency (int) – Maximum number of concurrent API calls, enforced by an
asyncio.Semaphore.prompt_resolver (Callable[[dict[str, Any], dict[str, dict[str, Any]]], tuple[str, set[str]]] | None) – Optional callback
(prompt_spec, results_by_name) -> (resolved_prompt, interpolated_names). When provided, each node’s prompt is resolved before execution. WhenNone, prompts are sent as-is.
- async execute(prompts)[source]
Build and execute a prompt dependency graph.
- Parameters:
prompts (list[dict[str, Any]]) – List of prompt dicts with keys: prompt_name, prompt, history, condition, abort_condition.
- Returns:
GraphResultwith per-prompt results and aggregate counts.- Raises:
ValueError – If a dependency cycle is detected.
- Return type:
- class ConditionEvaluator(results_by_name)[source]
Bases:
objectSafely evaluates condition expressions for conditional prompt execution.
Security model: Never uses eval() or exec() on user input. Conditions are parsed using AST and evaluated using a restricted set of operators, functions, and method calls.
- Syntax:
{{prompt_name.property}} == “value” {{prompt_name.property}} != “value” {{prompt_name.property}} contains “substring” {{prompt_name.property}} not contains “substring” {{prompt_name.property}} matches “regex” len({{prompt_name.response}}) > 100 {{a.status}} == “success” and {{b.status}} == “success” {{a.status}} == “success” or {{b.status}} == “success” not {{prompt_name.has_response}}
- String methods:
{{prompt_name.response}}.startswith(“prefix”) {{prompt_name.response}}.endswith(“suffix”) {{prompt_name.response}}.lower() == “value” {{prompt_name.response}}.split(“,”)[0]
- JSON functions:
json_get({{prompt_name.response}}, “key.nested[0]”) json_get_default({{prompt_name.response}}, “key”, “default”) json_has({{prompt_name.response}}, “key”) json_keys({{prompt_name.response}}) “key” in json_keys({{prompt_name.response}})
- Available properties:
status: “success”, “failed”, “skipped”
response: The AI response text
attempts: Number of retry attempts (int)
error: Error message if failed (str)
has_response: True if response exists and non-empty (bool)
Note
For JSON responses, use
json_has(),json_get(), or"key" in json_keys(...)to query structure. Thecontainsandinoperators always perform substring matching on the stringified response text, which can produce false matches against dict/list repr strings (e.g."True" in {{s.response}}matches"{'pass': True}"because the four characters appear in the repr, not because of a value).- ALLOWED_OPERATORS = {<class 'ast.And'>: <function ConditionEvaluator.<lambda>>, <class 'ast.Eq'>: <built-in function eq>, <class 'ast.Gt'>: <built-in function gt>, <class 'ast.GtE'>: <built-in function ge>, <class 'ast.Lt'>: <built-in function lt>, <class 'ast.LtE'>: <built-in function le>, <class 'ast.NotEq'>: <built-in function ne>, <class 'ast.Or'>: <function ConditionEvaluator.<lambda>>}
- ALLOWED_UNARY_OPERATORS = {<class 'ast.Not'>: <built-in function not_>}
- ALLOWED_FUNCTIONS = {'abs': <built-in function abs>, 'bool': <function ConditionEvaluator.<lambda>>, 'count': <function ConditionEvaluator.<lambda>>, 'find': <function ConditionEvaluator.<lambda>>, 'float': <function ConditionEvaluator.<lambda>>, 'int': <function ConditionEvaluator.<lambda>>, 'is_empty': <function ConditionEvaluator.<lambda>>, 'is_null': <function ConditionEvaluator.<lambda>>, 'json_get': <function ConditionEvaluator.<lambda>>, 'json_get_default': <function ConditionEvaluator.<lambda>>, 'json_has': <function ConditionEvaluator.<lambda>>, 'json_keys': <function ConditionEvaluator.<lambda>>, 'json_parse': <function ConditionEvaluator.<lambda>>, 'json_type': <function ConditionEvaluator.<lambda>>, 'json_values': <function ConditionEvaluator.<lambda>>, 'len': <built-in function len>, 'lower': <function ConditionEvaluator.<lambda>>, 'lstrip': <function ConditionEvaluator.<lambda>>, 'max': <built-in function max>, 'min': <built-in function min>, 'replace': <function ConditionEvaluator.<lambda>>, 'rfind': <function ConditionEvaluator.<lambda>>, 'round': <built-in function round>, 'rsplit': <function ConditionEvaluator.<lambda>>, 'rstrip': <function ConditionEvaluator.<lambda>>, 'slice': <function ConditionEvaluator.<lambda>>, 'split': <function ConditionEvaluator.<lambda>>, 'str': <function ConditionEvaluator.<lambda>>, 'strip': <function ConditionEvaluator.<lambda>>, 'trim': <function ConditionEvaluator.<lambda>>, 'upper': <function ConditionEvaluator.<lambda>>}
- ALLOWED_STRING_METHODS = frozenset({'capitalize', 'center', 'count', 'endswith', 'find', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'islower', 'isnumeric', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'replace', 'rfind', 'rindex', 'rjust', 'rsplit', 'rstrip', 'split', 'startswith', 'strip', 'title', 'upper', 'zfill'})
- ALLOWED_LIST_METHODS = frozenset({'count', 'index'})
- ALLOWED_DICT_METHODS = frozenset({'get', 'keys', 'values'})
- VARIABLE_PATTERN = re.compile('\\{\\{(\\w+)\\.(\\w+)\\}\\}')
- evaluate_with_trace(condition)[source]
Evaluate a condition expression and return the resolved trace.
Like evaluate(), but also returns the resolved expression showing what each {{name.property}} was substituted with.
- Parameters:
condition (str) – The condition string to evaluate.
- Returns:
Tuple of (result, error_message, resolved_trace). - result: True if condition passes, False otherwise. - error_message: None if successful, error string if failed. - resolved_trace: The condition after variable substitution,
e.g. ‘“failed” == “success”’, or None if no condition.
- Return type:
- class ConversationHistory[source]
Bases:
objectAPI-facing message history for provider SDK calls.
Unlike
PermanentHistory, turns here carry no timestamps and the history is intended to be cleared (e.g. when starting a new conversation context). Theget_turns()output is structured for direct injection into provider message APIs.- add_turn_assistant(content)[source]
Append an assistant turn to the history.
- Parameters:
content (str) – The assistant’s response text.
- Return type:
None
- class DependencyEdge(from_seq, to_seq, source, condition_text=None)[source]
Bases:
objectA single dependency edge in the execution graph.
- class Embeddings(model='mistral/mistral-embed', api_key=None, api_base=None, cache_enabled=True, cache_size=256, device='cpu', **kwargs)[source]
Bases:
objectCompute text embeddings using local or remote models.
Supports local sentence-transformer models (prefix
local/) and remote providers via LiteLLM (e.g.mistral/mistral-embed,openai/text-embedding-3-small). Results are optionally cached in an LRU-style in-memory cache.- Parameters:
model (str) – Model identifier. Use
local/<name>for local sentence-transformer models, or<provider>/<model>for remote APIs.api_key (str | None) – API key for the remote provider. If None, the key is read from a provider-specific environment variable.
api_base (str | None) – Optional custom API base URL.
cache_enabled (bool) – Whether to cache embedding results in memory.
cache_size (int) – Maximum number of entries in the embedding cache.
device (str) – Torch device for local models (e.g.
"cpu","cuda").**kwargs (Any) – Extra keyword arguments forwarded to the LiteLLM embedding call.
- clear_cache()[source]
Clear the embedding cache.
- Returns:
Number of entries that were in the cache before clearing.
- Return type:
- class ExecutionGraph(nodes=<factory>, edges=<factory>, max_level=0)[source]
Bases:
objectComplete execution graph with dependency metadata.
- Parameters:
nodes (dict[int, PromptNode])
edges (list[DependencyEdge])
max_level (int)
- nodes
Dictionary mapping sequence numbers to PromptNodes.
- Type:
- edges
List of all dependency edges with source information.
- nodes: dict[int, PromptNode]
- edges: list[DependencyEdge]
- class ExecutionState(completed=<factory>, in_progress=<factory>, pending=<factory>, results=<factory>, results_lock=<factory>, success_count=0, failed_count=0, skipped_count=0, aborted_count=0, results_by_name=<factory>, current_name='', aborted=False)[source]
Bases:
objectTracks state during parallel execution with thread-safe operations.
- Parameters:
- pending
Dict of pending PromptNodes by sequence number.
- Type:
- results_lock
Thread lock for result access.
- Type:
_thread.allocate_lock
- pending: dict[int, PromptNode]
- results_lock: allocate_lock
- class FFAIClientBase[source]
Bases:
ABCAbstract base class defining the contract for AI client implementations.
All AI provider clients (Mistral, Anthropic, OpenAI, etc.) must inherit from this class and implement its abstract methods.
- retry_config
Override retry configuration.
Noneuses the global config defaults.
- property last_usage: TokenUsage | None
Token usage from the most recent generate_response() call.
- property last_duration_ms: float | None
Wall-clock duration in ms of the most recent generate_response() call.
- add_tool_result(tool_call_id, content)[source]
Add a tool result to the conversation history.
Default implementation appends a tool-role message. Subclasses that manage conversation history differently (e.g. via an external API) should override this method.
- class GraphResult(results=<factory>, success_count=0, failed_count=0, skipped_count=0, aborted=False, aborted_count=0)[source]
Bases:
objectAggregate result from executing a prompt dependency graph.
- Parameters:
- results
Mapping of prompt_name to
ResponseResult.
- results: dict[str, ResponseResult]
- class HistoryExporter(history, clean_history, prompt_attr_history, ordered_history, persist_dir, persist_name=None, auto_persist=False)[source]
Bases:
objectDataFrame export and persistence for FFAI interaction histories.
Converts the various history stores into Polars DataFrames and handles persistence to Parquet files.
- Parameters:
history (list[dict[str, Any]]) – Raw interaction history list.
clean_history (list[dict[str, Any]]) – Cleaned interaction history list.
prompt_attr_history (list[dict[str, Any]]) – Prompt-attribute-indexed history list.
ordered_history (Any) – OrderedPromptHistory instance.
persist_dir (str) – Directory for persisted files.
persist_name (str | None) – Base name for persisted files.
auto_persist (bool) – Whether to auto-persist DataFrames on creation.
- history_to_dataframe()[source]
Convert the full interaction history to a polars DataFrame.
- Return type:
DataFrame
- clean_history_to_dataframe()[source]
Convert the clean interaction history to a polars DataFrame.
- Return type:
DataFrame
- prompt_attr_history_to_dataframe()[source]
Convert the prompt attribute history to a polars DataFrame.
- Return type:
DataFrame
- ordered_history_to_dataframe()[source]
Convert the ordered interaction history to a polars DataFrame.
- Return type:
DataFrame
- search_history(text=None, prompt_name=None, model=None, start_time=None, end_time=None)[source]
Search interaction history with flexible filtering options.
- Parameters:
text (str | None) – Text to search for in prompts and responses
prompt_name (str | None) – Filter by prompt name
model (str | None) – Filter by model name
start_time (float | None) – Filter by timestamp (start time in epoch seconds)
end_time (float | None) – Filter by timestamp (end time in epoch seconds)
- Returns:
Filtered dataframe of interactions
- Return type:
pl.DataFrame
- get_prompt_name_stats_df(prompt_name_stats)[source]
Get statistics on prompt name usage as a DataFrame.
- get_response_length_stats()[source]
Get statistics on response lengths by prompt name.
- Returns:
DataFrame with response length statistics by prompt name
- Return type:
pl.DataFrame
- class Memory(embeddings, store=None)[source]
Bases:
objectSemantic recall over completed conversation turns.
Wraps an
EmbeddingBackendand aTurnVectorStore. Provides two indexing entry points:index_turn()/aindex_turn()— extract text from a structuredturndict viaturn["content"][0]["text"].index_turn_text()/aindex_turn_text()— embed an arbitrary caller-supplied string while still storing the structuredturndict alongside. Used byHistoryRecorder(L4) to embed the Q+A pair (f"{prompt}\n{response}") rather than just the response.
- Parameters:
embeddings (EmbeddingBackend) – Embedding backend (LiteLLM API model or local
local/...model). Any object implementingEmbeddingBackendis accepted.store (TurnVectorStore | None) – Optional
TurnVectorStore. Defaults to a fresh instance. Public and settable so callers can swap in a store loaded from Parquet (L3).
- index_turn_text(text, turn, metadata=None)[source]
Embed an arbitrary
textand store the structuredturn.Use this when the embedded text differs from
turn["content"][0]["text"]— e.g., when embedding the Q+A pair (f"{prompt}\n{response}") while still storing the response as the canonical turn content.
- async aindex_turn(turn, metadata=None)[source]
Async variant of
index_turn().
- async aindex_turn_text(text, turn, metadata=None)[source]
Async variant of
index_turn_text().
- reindex(new_embeddings)[source]
Re-embed all stored texts with a new embedding model.
Reads all entries via
TurnVectorStore.iter_entries(), clears the store, and re-adds each entry with the new embedding. The turn dicts and metadata are preserved verbatim. After reindexing,self._embeddingsis updated so subsequentsearch()calls embed queries with the new model.- Parameters:
new_embeddings (EmbeddingBackend) – The new embedding backend to use.
- Return type:
None
- class OrderedPromptHistory[source]
Bases:
objectManages ordered prompt-response history with named references.
This class provides: - Named prompt storage for declarative context assembly - Sequential ordering of interactions - Query capabilities by prompt name, model, etc. - History chain tracking for dependency resolution
- prompt_dict
OrderedDict mapping prompt names to interaction lists.
- prompt_dict: OrderedDict[str, list[Interaction]]
- get_effective_prompt_name(prompt_name)[source]
Get the effective prompt name from various input types.
- add_interaction(model, prompt, response, prompt_name=None, history=None)[source]
Add a new interaction to the history, storing cleaned versions of prompt and response.
- Parameters:
model (str) – The model used for the interaction
prompt (str) – The prompt text
response (str) – The response text
prompt_name (str | None) – Optional name/key for the prompt. If None, uses prompt text as key
history (list[str] | None) – Optional list of prompt names that form the history chain for this interaction
- Returns:
The created Interaction object
- Return type:
- get_interactions_by_prompt_name(prompt_name)[source]
Get all interactions for a specific prompt name.
- Parameters:
prompt_name (str)
- Return type:
- get_latest_interaction_by_prompt_name(prompt_name)[source]
Get the most recent interaction for a specific prompt name.
- Parameters:
prompt_name (str)
- Return type:
Interaction | None
- get_interactions_by_model_and_prompt_name(model, prompt_name)[source]
Get all interactions for a specific model and prompt name combination.
- Parameters:
- Return type:
- merge_histories(other)[source]
Merge another OrderedPromptHistory into this one.
- Parameters:
other (OrderedPromptHistory) – Another OrderedPromptHistory instance to merge
- Return type:
None
- get_interaction_by_prompt(prompt)[source]
Get an interaction by its exact prompt text.
Useful when prompt was used as the prompt_name.
- Parameters:
prompt (str)
- Return type:
Interaction | None
- class PermanentHistory[source]
Bases:
objectAppend-only chronological turn history with timestamps.
Each turn stores a
role("user"or"assistant"), structuredcontent, a per-turntimestamp, and optionalmetadata. Consecutive user turns are coalesced by appending content unless the caller passes non-Nonemetadata, in which case a new turn is always created (so distinctprompt_namemetadata is never silently merged).- add_turn_assistant(content, metadata=None)[source]
Append an assistant turn with the current timestamp.
- add_turn_user(content, metadata=None)[source]
Append a user turn, coalescing with the previous user turn when adjacent.
Coalescing only happens when metadata is
None. If metadata is provided, a new turn is always created so distinctprompt_namemetadata is never silently merged into the previous user turn.
- class PromptBuilder(prompt_attr_history)[source]
Bases:
objectBuilds final prompts with history context and variable interpolation.
Reads from the shared prompt-attribute history to resolve
{{prompt_name.response}}patterns and assemble conversation context blocks.- build_prompt(prompt, history=None, dependencies=None, strict=False)[source]
Build the final prompt with history and variable interpolation.
- Parameters:
prompt (str) – The prompt template (may contain {{}} interpolation patterns)
history (list[str] | None) – List of prompt names to include in conversation history
dependencies (Any | None) – Additional dependencies (unused but kept for compatibility)
strict (bool) – If True, raise ValueError on unknown {{name.response}} references
- Returns:
Tuple of (final_prompt, set_of_interpolated_prompt_names)
- Return type:
- class PromptNode(sequence, prompt, dependencies=<factory>, level=0)[source]
Bases:
objectRepresents a prompt in the execution dependency graph.
- class ResponseContext(shared_prompt_attr_history=None, history_lock=None)[source]
Bases:
objectManages the shared
prompt_attr_historylist with thread-safe recording.This class centralises all mutation of the shared prompt-attribute history that
PromptBuilderreads from for{{name.response}}interpolation. It replaces the inline history recording previously scattered throughFFAI.generate_response()and direct list appends inPlanningRunner/SynthesisRunner.- Parameters:
shared_prompt_attr_history (list[dict[str, Any]] | None) – Optional pre-existing list to share. When
Nonea new empty list is created.history_lock (threading.Lock | None) – Optional lock for thread-safe access.
- record(prompt, response, model, prompt_name=None, history=None)[source]
Record an interaction to
prompt_attr_history.If response is a
dict(JSON response from the LLM), each top-level key-value pair is recorded as a separate entry so that{{key_name.response}}interpolation works.- Parameters:
prompt (str) – The original prompt text (or JSON key name).
response (Any) – The cleaned response (str, dict, etc.).
model (str) – Model identifier used for this call.
prompt_name (str | None) – Optional logical name for the prompt.
history (list[str] | None) – Optional list of history dependency names.
- Return type:
None
- record_raw(interaction)[source]
Append a pre-built interaction dict.
Used by
PlanningRunnerandSynthesisRunnerto inject results that were not produced byFFAI.generate_response().
- property history_lock: threading.Lock | None
The lock used for thread-safe history access.
- class ResponseResult(response, resolved_prompt='', usage=None, cost_usd=0.0, model='', duration_ms=0.0, status='success', condition_trace=None, condition_error=None, parsed=None, parsing_errors=None)[source]
Bases:
objectStructured result from an FFAI response generation call.
Replaces side-channel attributes (
last_usage,last_cost_usd,last_resolved_prompt) with a single typed return value.- Parameters:
- response
The cleaned AI response (str, dict, list, etc.).
- Type:
Any
- usage
Token usage from the API call, if available.
- Type:
ffai.core.usage.TokenUsage | None
- parsed
Validated Pydantic model instance (when response_model is used).
- Type:
Any
- parsing_errors
Validation error strings if structured output parsing failed.
- usage: TokenUsage | None = None
- class StructuredOutputHandler(max_retries=2)[source]
Bases:
objectValidates LLM responses against Pydantic models.
- Parameters:
max_retries (int) – Maximum number of re-prompt attempts after validation failure. Total attempts = max_retries + 1.
- build_response_format(model)[source]
Return the Pydantic model class for use as
response_format.LiteLLM’s
completion()acceptstype[BaseModel]directly in theresponse_formatparameter and handles provider-specific schema translation internally. Returning the model class instead of a hand-built dict avoids incompatible schemas across providers.
- build_system_suffix(model)[source]
Generate instruction text describing the expected JSON schema.
Intended to be appended to system instructions so the model knows the exact output shape.
- validate(response, model)[source]
Parse and validate an LLM response against a Pydantic model.
Uses
json_repairfor fault-tolerant JSON parsing, then Pydantic validation for type safety.- Parameters:
- Returns:
StructuredResultwithparsedset on success, orparsing_errorspopulated on failure.- Return type:
- prepare_retry_state(prompt, response_model)[source]
Initialize retry state for a structured output loop.
- process_attempt(response, response_model, prompt, attempt, all_errors, best_result)[source]
Process a single structured output attempt.
- Parameters:
response (str) – The raw LLM response.
response_model (type[BaseModel]) – Pydantic BaseModel subclass.
prompt (str) – The original resolved prompt (for feedback).
attempt (int) – Zero-indexed attempt number.
best_result (StructuredResult | None) – Best result so far.
- Returns:
Tuple of (updated_best_result, next_prompt, updated_errors, should_stop).
- Return type:
- finalize_retry(best_result, all_errors, max_attempts)[source]
Finalize after all retry attempts exhausted.
- Parameters:
best_result (StructuredResult | None) – Last validation result.
max_attempts (int) – Total attempts made.
- Returns:
Final
StructuredResult.- Return type:
- class StructuredResult(parsed, raw_response, attempts, parsing_errors=<factory>)[source]
Bases:
objectOutcome of a structured-output validation attempt.
- parsed
Validated Pydantic model instance, or None on failure.
- Type:
pydantic.main.BaseModel | None
- class TurnHit(score, turn, turn_index, text, metadata)[source]
Bases:
objectA single ranked result returned by
TurnVectorStore.search.- Parameters:
- score
Cosine similarity in
[-1.0, 1.0]between the query embedding and the stored turn’s embedding. Sorted descending bysearch().- Type:
- turn
The raw turn dict stored at index time. Mirrors the
PermanentHistoryturn shape:{"role": str, "content": [{"type": "text", "text": str}], "timestamp": float, "metadata": dict}.
- turn_index
Position of the entry in
TurnVectorStoreat the time of the search. Stable within a singlesearch()call for a given store; may shift afterclear().- Type:
- text
Pre-extracted plain text (typically the Q+A pair) that was embedded. Equal to the string passed to
add(text=...).- Type:
- metadata
Caller-provided metadata attached at index time. Always present (possibly empty dict). Tier 2 will populate
user_id/session_id/agent_idhere.
- class TurnVectorStore[source]
Bases:
objectAppend-only in-memory store of embedded turns with cosine search.
Entries are stored in four parallel lists keyed by an integer index.
add()returns the index of the appended entry; that index is preserved on the correspondingTurnHitreturned bysearch().Search is O(N) over all stored entries. Adequate for Tier 1 scale; a vector-index backend (Chroma/Qdrant) is a future concern.
- add(text, embedding, turn, metadata=None)[source]
Append an entry and return its integer index.
- Parameters:
- Returns:
The integer index of the newly appended entry.
- Return type:
- search(query_embedding, top_k=5, threshold=None)[source]
Return up to
top_kturns ranked by cosine similarity.- Parameters:
- Returns:
Hits sorted by
scoredescending. Empty list if the store is empty or all hits fall belowthreshold.- Return type:
Functions
- build_execution_graph(prompts)[source]
Build dependency graph for parallel execution.
Delegates to build_execution_graph_with_edges and returns only the nodes dict for backward compatibility.
- Parameters:
prompts (Sequence[dict[str, Any]]) – List of prompt dictionaries with sequence, prompt_name, history, and condition fields.
- Returns:
Dictionary mapping sequence numbers to PromptNodes.
- Raises:
ValueError – If a dependency cycle is detected.
- Return type:
- build_execution_graph_with_edges(prompts)[source]
Build execution graph with full edge source metadata.
- Parameters:
prompts (Sequence[dict[str, Any]]) – List of prompt dictionaries.
- Returns:
ExecutionGraph with nodes, edges, and max_level.
- Raises:
ValueError – If a dependency cycle is detected.
- Return type:
- build_graph_history_dict(results_by_name)[source]
Build a
{name: response_text}mapping from executor results.
- check_abort_condition(prompt_spec, results_by_name)[source]
Check if a prompt’s
abort_conditiontriggers a cascade abort.
- clean_response(response)[source]
Process and validate a response, removing think tags and extracting JSON.
- evaluate_condition(prompt, results_by_name, condition_field='condition')[source]
Evaluate a prompt’s condition.
- Parameters:
- Returns:
Tuple of (should_execute, condition_result, condition_error).
- Return type:
- evaluate_condition_with_trace(prompt, results_by_name, condition_field='condition')[source]
Evaluate a prompt’s condition and return the resolved trace.
- Parameters:
- Returns:
Tuple of (should_execute, condition_result, condition_error, condition_trace).
- Return type:
- extract_json(text)[source]
Extract JSON from text using fault-tolerant
json_repair.Handles markdown code blocks, trailing commas, unquoted keys, and other common LLM JSON mistakes.
- extract_json_field(data, path)[source]
Extract a value from JSON using dot notation path.
Supports: - Simple fields: “field_name” - Nested objects: “object.field” - Array indices: “array.0” - Combined: “object.array.0.field”
- get_ready_prompts(state, nodes)[source]
Get prompts ready for execution (all dependencies completed).
- Parameters:
state (ExecutionState) – Current execution state.
nodes (dict[int, PromptNode]) – Execution graph nodes.
- Returns:
List of PromptNodes ready for execution, sorted by level and sequence.
- Return type:
- interpolate_prompt(prompt, history, strict=False)[source]
Replace {{prompt_name.response}} patterns with actual content.
- Parameters:
- Returns:
Tuple of (resolved_prompt, set_of_interpolated_prompt_names)
- Return type:
- resolve_graph_prompt(prompt_spec, results_by_name)[source]
Resolve interpolation and history injection for a graph node.
Performs two-phase prompt assembly:
Interpolation: Resolves
{{name.response}}patterns using results from earlier DAG levels.History injection: For each name in
prompt_spec["history"], formats the prior Q&A as<conversation_history>XML. Entries already interpolated are deduplicated.
- run_sync(coro)[source]
Run an async coroutine from synchronous code.
If no event loop is running, delegates to
asyncio.run(). If an event loop is already running (e.g. inside a Jupyter notebook or an existing async framework), runs the coroutine in a background thread to avoidRuntimeError.
- should_skip_for_failed_deps(node, results_by_name, nodes)[source]
Check if a node should be skipped because any dependency failed.
Only failed dependencies trigger a skip. Skipped dependencies do NOT cascade — a node whose only dependency was skipped will still execute (its condition will determine whether it should proceed).
- Parameters:
node (PromptNode) – The prompt node to check.
results_by_name (dict[str, dict[str, Any]]) – Accumulated results indexed by prompt_name.
nodes (dict[int, PromptNode]) – All graph nodes indexed by sequence number.
- Returns:
Tuple of (should_skip, reason_string).
- Return type: