agentforge-core 0.2.1__py3-none-any.whl
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.
- agentforge_core/__init__.py +228 -0
- agentforge_core/_bm25.py +132 -0
- agentforge_core/config/__init__.py +62 -0
- agentforge_core/config/loader.py +239 -0
- agentforge_core/config/module_schemas.py +208 -0
- agentforge_core/config/schema.py +424 -0
- agentforge_core/contracts/__init__.py +52 -0
- agentforge_core/contracts/auth.py +33 -0
- agentforge_core/contracts/chat.py +118 -0
- agentforge_core/contracts/embedding.py +71 -0
- agentforge_core/contracts/evaluator.py +56 -0
- agentforge_core/contracts/finding.py +39 -0
- agentforge_core/contracts/graph_store.py +180 -0
- agentforge_core/contracts/guardrails.py +129 -0
- agentforge_core/contracts/llm.py +152 -0
- agentforge_core/contracts/memory.py +113 -0
- agentforge_core/contracts/migrator.py +120 -0
- agentforge_core/contracts/renderer.py +57 -0
- agentforge_core/contracts/reranker.py +91 -0
- agentforge_core/contracts/strategy.py +70 -0
- agentforge_core/contracts/task.py +73 -0
- agentforge_core/contracts/tool.py +71 -0
- agentforge_core/contracts/vector_store.py +151 -0
- agentforge_core/migrations/__init__.py +14 -0
- agentforge_core/migrations/discover.py +77 -0
- agentforge_core/migrations/template.py +34 -0
- agentforge_core/observability/__init__.py +18 -0
- agentforge_core/observability/tracing.py +37 -0
- agentforge_core/production/__init__.py +77 -0
- agentforge_core/production/budget.py +134 -0
- agentforge_core/production/exceptions.py +136 -0
- agentforge_core/production/fallback.py +321 -0
- agentforge_core/production/log_filter.py +49 -0
- agentforge_core/production/log_format.py +117 -0
- agentforge_core/production/run_context.py +108 -0
- agentforge_core/py.typed +0 -0
- agentforge_core/resolver/__init__.py +38 -0
- agentforge_core/resolver/discover.py +145 -0
- agentforge_core/resolver/resolve.py +168 -0
- agentforge_core/testing/__init__.py +45 -0
- agentforge_core/testing/conformance.py +1138 -0
- agentforge_core/values/__init__.py +103 -0
- agentforge_core/values/auth.py +20 -0
- agentforge_core/values/chat.py +131 -0
- agentforge_core/values/claim.py +30 -0
- agentforge_core/values/graph.py +136 -0
- agentforge_core/values/guardrails.py +49 -0
- agentforge_core/values/manifest.py +129 -0
- agentforge_core/values/messages.py +153 -0
- agentforge_core/values/module.py +40 -0
- agentforge_core/values/pipeline.py +43 -0
- agentforge_core/values/retrieval.py +53 -0
- agentforge_core/values/state.py +118 -0
- agentforge_core/values/vector.py +59 -0
- agentforge_core-0.2.1.dist-info/METADATA +66 -0
- agentforge_core-0.2.1.dist-info/RECORD +58 -0
- agentforge_core-0.2.1.dist-info/WHEEL +4 -0
- agentforge_core-0.2.1.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Provider-agnostic message and response shapes for the LLM contract.
|
|
2
|
+
|
|
3
|
+
Every `LLMClient` returns `LLMResponse`; every reasoning strategy
|
|
4
|
+
operates on `list[Message]` regardless of which provider backs the
|
|
5
|
+
agent. Streaming clients yield `StreamChunk`s; embedding clients
|
|
6
|
+
return `EmbeddingResponse`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any, Literal
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
14
|
+
|
|
15
|
+
StreamChunkKind = Literal["text", "tool_call", "stop", "thinking"]
|
|
16
|
+
"""Closed enum of stream-chunk kinds. Provider drivers normalise their
|
|
17
|
+
event streams into a sequence of these chunks. Adding a new kind is a
|
|
18
|
+
minor version bump."""
|
|
19
|
+
|
|
20
|
+
MessageRole = Literal["system", "user", "assistant", "tool"]
|
|
21
|
+
"""Allowed message roles. Mirrors the Anthropic / OpenAI common set."""
|
|
22
|
+
|
|
23
|
+
StopReason = Literal["end_turn", "tool_use", "max_tokens", "stop_sequence", "other"]
|
|
24
|
+
"""Provider-normalised reason the LLM stopped emitting tokens."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Message(BaseModel):
|
|
28
|
+
"""One turn in the chat-completion exchange."""
|
|
29
|
+
|
|
30
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
31
|
+
|
|
32
|
+
role: MessageRole
|
|
33
|
+
content: str
|
|
34
|
+
name: str | None = None
|
|
35
|
+
tool_call_id: str | None = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ToolCall(BaseModel):
|
|
39
|
+
"""A tool invocation emitted by the LLM."""
|
|
40
|
+
|
|
41
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
42
|
+
|
|
43
|
+
id: str
|
|
44
|
+
name: str
|
|
45
|
+
arguments: dict[str, Any] = Field(default_factory=dict)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ToolSpec(BaseModel):
|
|
49
|
+
"""Provider-agnostic tool description sent to the LLM.
|
|
50
|
+
|
|
51
|
+
`schema` is the JSON-schema dict (typically from a Pydantic model's
|
|
52
|
+
`model_json_schema()`). The `Tool` ABC's `to_spec()` produces this.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
56
|
+
|
|
57
|
+
name: str
|
|
58
|
+
description: str
|
|
59
|
+
schema_: dict[str, Any] = Field(alias="schema")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class TokenUsage(BaseModel):
|
|
63
|
+
"""Token accounting from a single LLM call."""
|
|
64
|
+
|
|
65
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
66
|
+
|
|
67
|
+
input_tokens: int = Field(ge=0)
|
|
68
|
+
output_tokens: int = Field(ge=0)
|
|
69
|
+
cache_read_tokens: int = Field(default=0, ge=0)
|
|
70
|
+
cache_write_tokens: int = Field(default=0, ge=0)
|
|
71
|
+
thinking_tokens: int = Field(default=0, ge=0)
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def total(self) -> int:
|
|
75
|
+
"""Sum of input + output tokens (excludes cache and thinking metadata)."""
|
|
76
|
+
return self.input_tokens + self.output_tokens
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class LLMResponse(BaseModel):
|
|
80
|
+
"""Provider-agnostic response from one LLM call."""
|
|
81
|
+
|
|
82
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
83
|
+
|
|
84
|
+
content: str
|
|
85
|
+
tool_calls: tuple[ToolCall, ...] = ()
|
|
86
|
+
stop_reason: StopReason
|
|
87
|
+
usage: TokenUsage
|
|
88
|
+
cost_usd: float = Field(ge=0.0)
|
|
89
|
+
model: str
|
|
90
|
+
provider: str
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class StreamChunk(BaseModel):
|
|
94
|
+
"""One event in a provider's streaming response.
|
|
95
|
+
|
|
96
|
+
Streaming `LLMClient`s yield an `AsyncIterator[StreamChunk]`. The
|
|
97
|
+
chunks are ordered: text deltas, optional thinking blocks, optional
|
|
98
|
+
tool-call deltas, and exactly one terminal `stop` chunk carrying
|
|
99
|
+
the final usage and cost. Consumers that don't care about
|
|
100
|
+
streaming can accumulate chunks into a single `LLMResponse` via
|
|
101
|
+
a helper.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
105
|
+
|
|
106
|
+
kind: StreamChunkKind
|
|
107
|
+
delta: str = ""
|
|
108
|
+
"""Incremental content for `text` and `thinking` kinds (empty for
|
|
109
|
+
`tool_call` and `stop`)."""
|
|
110
|
+
|
|
111
|
+
tool_call: ToolCall | None = None
|
|
112
|
+
"""The fully-assembled `ToolCall` for `kind == "tool_call"` chunks.
|
|
113
|
+
Provider drivers buffer tool-call argument streams internally and
|
|
114
|
+
emit one chunk once the call is complete."""
|
|
115
|
+
|
|
116
|
+
stop_reason: StopReason | None = None
|
|
117
|
+
"""Set on the terminal `stop` chunk; `None` otherwise."""
|
|
118
|
+
|
|
119
|
+
usage: TokenUsage | None = None
|
|
120
|
+
"""Final token accounting on the terminal `stop` chunk."""
|
|
121
|
+
|
|
122
|
+
cost_usd: float = Field(default=0.0, ge=0.0)
|
|
123
|
+
"""Final cost on the terminal `stop` chunk; `0.0` on intermediate
|
|
124
|
+
chunks."""
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class EmbeddingResponse(BaseModel):
|
|
128
|
+
"""Provider-agnostic response from an embedding call.
|
|
129
|
+
|
|
130
|
+
`vectors` is a list of float lists — one vector per input text in
|
|
131
|
+
the same order the texts were passed. Every vector has the same
|
|
132
|
+
`dimensions` (the model-declared dimensionality).
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
136
|
+
|
|
137
|
+
vectors: tuple[tuple[float, ...], ...]
|
|
138
|
+
"""One vector per input text, in input order. Tuples-of-tuples
|
|
139
|
+
keeps the response frozen and hashable while the dimensionality
|
|
140
|
+
stays uniform across vectors."""
|
|
141
|
+
|
|
142
|
+
dimensions: int = Field(ge=1)
|
|
143
|
+
"""Length of every vector. The `EmbeddingClient.dimensions()`
|
|
144
|
+
accessor declares this up front for callers that need to size
|
|
145
|
+
storage before the call."""
|
|
146
|
+
|
|
147
|
+
usage: TokenUsage
|
|
148
|
+
"""Token accounting (input only — embeddings produce no output
|
|
149
|
+
tokens)."""
|
|
150
|
+
|
|
151
|
+
cost_usd: float = Field(ge=0.0)
|
|
152
|
+
model: str
|
|
153
|
+
provider: str
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""`ModuleInfo` — descriptor for a registered module (feat-010).
|
|
2
|
+
|
|
3
|
+
Returned by `Resolver.list_installed`. Carries enough metadata for
|
|
4
|
+
the `agentforge list modules` CLI to render a useful table:
|
|
5
|
+
the entry-point category + name, the providing distribution + its
|
|
6
|
+
version, and the class object itself (for introspection — e.g.
|
|
7
|
+
capabilities, docstring).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ModuleInfo(BaseModel):
|
|
16
|
+
"""Metadata about one registered module.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
category: Entry-point group suffix — e.g. `"providers"`,
|
|
20
|
+
`"memory"`, `"tools"`, `"hooks"`, `"renderers"`,
|
|
21
|
+
`"evaluators"`. Matches the second arg to `register`.
|
|
22
|
+
name: Per-category identifier — e.g. `"bedrock"`, `"sqlite"`.
|
|
23
|
+
package: Distribution that provided the class, if known
|
|
24
|
+
(`"agentforge-bedrock"`, `"agentforge"`, etc.). `None`
|
|
25
|
+
for classes registered via `@register` from an unpackaged
|
|
26
|
+
location (typical in tests or single-file agents).
|
|
27
|
+
version: Distribution version (`"0.2.1"`). `None` when
|
|
28
|
+
`package` is `None`.
|
|
29
|
+
cls_qualname: Fully-qualified class name (`"agentforge_bedrock.client.BedrockClient"`)
|
|
30
|
+
— useful for diagnostic output without pulling the class
|
|
31
|
+
object into the renderer.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
35
|
+
|
|
36
|
+
category: str = Field(min_length=1)
|
|
37
|
+
name: str = Field(min_length=1)
|
|
38
|
+
package: str | None = None
|
|
39
|
+
version: str | None = None
|
|
40
|
+
cls_qualname: str = Field(min_length=1)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""`PipelineResult` — the locked output of `Pipeline.run()`.
|
|
2
|
+
|
|
3
|
+
feat-015 ships this as a frozen Pydantic value model. The runtime
|
|
4
|
+
stores it on the agent for the duration of one run (system prompt
|
|
5
|
+
addendum + ``pipeline_findings`` built-in tool) and optionally
|
|
6
|
+
records it as a single ``__pipeline`` claim when ``record_runs`` is
|
|
7
|
+
configured.
|
|
8
|
+
|
|
9
|
+
`findings` is a list of `Finding` Protocol-compatible objects (the
|
|
10
|
+
shipped variants from ``agentforge.findings`` all satisfy it). The
|
|
11
|
+
shape stays tolerant of custom finding subclasses.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PipelineResult(BaseModel):
|
|
22
|
+
"""Consolidated output of one `Pipeline.run()` call.
|
|
23
|
+
|
|
24
|
+
Fields:
|
|
25
|
+
findings: All findings emitted across every task, in the
|
|
26
|
+
order each task completed. Task-failure entries (when
|
|
27
|
+
``on_task_error="continue"``) appear here too as
|
|
28
|
+
``SimpleFinding(category="pipeline.task_failure")``.
|
|
29
|
+
task_durations_ms: Per-task wallclock duration in
|
|
30
|
+
milliseconds.
|
|
31
|
+
task_failures: Map of task name → error message for any
|
|
32
|
+
task that raised. Empty when every task succeeded.
|
|
33
|
+
total_cost_usd: Sum of ``cost_estimate_usd`` across all
|
|
34
|
+
tasks that ran (the engine charges this against the
|
|
35
|
+
agent's budget).
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
39
|
+
|
|
40
|
+
findings: tuple[Any, ...] = ()
|
|
41
|
+
task_durations_ms: dict[str, int] = Field(default_factory=dict)
|
|
42
|
+
task_failures: dict[str, str] = Field(default_factory=dict)
|
|
43
|
+
total_cost_usd: float = Field(default=0.0, ge=0.0)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Retrieval-ergonomics value types (feat-023).
|
|
2
|
+
|
|
3
|
+
`GraphExpansion` bundles the knobs callers pass when wiring a
|
|
4
|
+
`Retriever` with graph-augmented expansion — the GraphStore to
|
|
5
|
+
traverse, the hop budget, edge-type filtering, the node property
|
|
6
|
+
to use as match text, and the per-hop score decay.
|
|
7
|
+
|
|
8
|
+
Kept frozen + strict per the framework's value-type policy. The
|
|
9
|
+
`store` field tolerates an `ABC` instance via
|
|
10
|
+
`arbitrary_types_allowed=True` — `GraphStore` isn't itself a
|
|
11
|
+
Pydantic model.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
17
|
+
|
|
18
|
+
from agentforge_core.contracts.graph_store import GraphStore
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class GraphExpansion(BaseModel):
|
|
22
|
+
"""Graph-traversal configuration for :class:`Retriever`
|
|
23
|
+
post-retrieve augmentation (feat-023).
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
store: The `GraphStore` driver to traverse.
|
|
27
|
+
``VectorMatch.id`` ↔ ``GraphNode.id`` alignment is a
|
|
28
|
+
caller contract; mismatched ids silently skip
|
|
29
|
+
expansion for that seed.
|
|
30
|
+
max_hops: Maximum traversal depth (>= 1). Each hop
|
|
31
|
+
multiplies the candidate set by the average graph
|
|
32
|
+
fan-out — tune cautiously.
|
|
33
|
+
edge_types: If set, restricts traversal to these edge
|
|
34
|
+
types. ``None`` means all edge types.
|
|
35
|
+
text_property: Graph-node property used to populate the
|
|
36
|
+
synthesised ``VectorMatch.text``. Defaults to
|
|
37
|
+
``"text"``.
|
|
38
|
+
decay: Per-hop score decay factor in ``(0, 1]``. An
|
|
39
|
+
expansion node at depth `d` gets score
|
|
40
|
+
``seed.score * decay ** d``. Default 0.5.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
model_config = ConfigDict(
|
|
44
|
+
frozen=True,
|
|
45
|
+
strict=True,
|
|
46
|
+
arbitrary_types_allowed=True,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
store: GraphStore
|
|
50
|
+
max_hops: int = Field(default=2, ge=1)
|
|
51
|
+
edge_types: tuple[str, ...] | None = None
|
|
52
|
+
text_property: str = Field(default="text", min_length=1)
|
|
53
|
+
decay: float = Field(default=0.5, gt=0.0, le=1.0)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""`AgentState`, `Step`, `RunResult` — the trace shape of an agent run.
|
|
2
|
+
|
|
3
|
+
Per ADR-0008, `state.steps` is uniform across reasoning strategies so
|
|
4
|
+
debugging skills transfer between agents that use different loop
|
|
5
|
+
shapes (ReAct, Plan-Execute, ToT, Multi-Agent).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from datetime import UTC, datetime
|
|
11
|
+
from typing import Any, Literal
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
14
|
+
|
|
15
|
+
from agentforge_core.contracts.evaluator import EvalResult
|
|
16
|
+
from agentforge_core.values.messages import ToolCall
|
|
17
|
+
|
|
18
|
+
StepKind = Literal[
|
|
19
|
+
"think",
|
|
20
|
+
"act",
|
|
21
|
+
"observe",
|
|
22
|
+
"plan",
|
|
23
|
+
"synthesize",
|
|
24
|
+
"branch",
|
|
25
|
+
"delegate",
|
|
26
|
+
"system",
|
|
27
|
+
]
|
|
28
|
+
"""Closed enum of step kinds. Every reasoning strategy emits steps from
|
|
29
|
+
this set; new kinds require a feature doc + minor version bump."""
|
|
30
|
+
|
|
31
|
+
FinishReason = Literal[
|
|
32
|
+
"completed",
|
|
33
|
+
"iteration_cap",
|
|
34
|
+
"budget_exceeded",
|
|
35
|
+
"guardrail",
|
|
36
|
+
"pipeline",
|
|
37
|
+
"error",
|
|
38
|
+
"cancelled",
|
|
39
|
+
]
|
|
40
|
+
"""How a run terminated. Mirrors the runtime's branching of
|
|
41
|
+
`BudgetExceeded` / `GuardrailViolation` / `PipelineFailure` / clean
|
|
42
|
+
completion."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Step(BaseModel):
|
|
46
|
+
"""One unit of progress in `AgentState.steps`."""
|
|
47
|
+
|
|
48
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
49
|
+
|
|
50
|
+
iteration: int = Field(ge=0)
|
|
51
|
+
kind: StepKind
|
|
52
|
+
content: str | dict[str, Any]
|
|
53
|
+
tool_call: ToolCall | None = None
|
|
54
|
+
tokens_in: int = Field(default=0, ge=0)
|
|
55
|
+
tokens_out: int = Field(default=0, ge=0)
|
|
56
|
+
cost_usd: float = Field(default=0.0, ge=0.0)
|
|
57
|
+
duration_ms: int = Field(default=0, ge=0)
|
|
58
|
+
timestamp: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
59
|
+
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class AgentState(BaseModel):
|
|
63
|
+
"""Mutable per-run state passed through the reasoning loop.
|
|
64
|
+
|
|
65
|
+
Strategies append to `steps`; tools and pipeline tasks may append
|
|
66
|
+
to `findings`. The runtime owns the lifecycle.
|
|
67
|
+
|
|
68
|
+
The per-run execution context (`RuntimeContext` — LLM client,
|
|
69
|
+
tools, memory, budget, system prompt) is stored on
|
|
70
|
+
`state.metadata` under the key `"__agentforge_runtime__"`,
|
|
71
|
+
populated by `Agent.run()` before calling the strategy.
|
|
72
|
+
Strategies access it via the `get_runtime(state)` helper in
|
|
73
|
+
`agentforge.strategies._base`. Storing it on metadata keeps
|
|
74
|
+
`agentforge-core` free of dependencies on runtime modules.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
model_config = ConfigDict(strict=True, validate_assignment=True)
|
|
78
|
+
|
|
79
|
+
run_id: str
|
|
80
|
+
task: str
|
|
81
|
+
steps: list[Step] = Field(default_factory=list)
|
|
82
|
+
findings: list[Any] = Field(default_factory=list)
|
|
83
|
+
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class RunResult(BaseModel):
|
|
87
|
+
"""Final, immutable output of `Agent.run()`.
|
|
88
|
+
|
|
89
|
+
Carries the agent's answer plus full trace, cost accounting, and the
|
|
90
|
+
run's `run_id` for cross-system correlation (ADR-0010).
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
94
|
+
|
|
95
|
+
output: str | dict[str, Any]
|
|
96
|
+
findings: tuple[Any, ...] = ()
|
|
97
|
+
steps: tuple[Step, ...] = ()
|
|
98
|
+
cost_usd: float = Field(ge=0.0)
|
|
99
|
+
tokens_in: int = Field(ge=0)
|
|
100
|
+
tokens_out: int = Field(ge=0)
|
|
101
|
+
run_id: str
|
|
102
|
+
duration_ms: int = Field(ge=0)
|
|
103
|
+
finish_reason: FinishReason = "completed"
|
|
104
|
+
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
105
|
+
guardrail_events: tuple[dict[str, Any], ...] = ()
|
|
106
|
+
"""feat-018: one entry per guardrail decision (input / output /
|
|
107
|
+
tool-gate). Each event carries `validator`, `passed`, `violations`,
|
|
108
|
+
`action`, `stage` ("input" / "output" / "tool"), and a hash of the
|
|
109
|
+
content (full content is never persisted here)."""
|
|
110
|
+
|
|
111
|
+
eval_scores: tuple[EvalResult, ...] = ()
|
|
112
|
+
"""Per-evaluator `EvalResult` from the post-run evaluator pass.
|
|
113
|
+
|
|
114
|
+
Ordered by the order evaluators were configured on the Agent.
|
|
115
|
+
Empty when no evaluators ran (no `evaluators=` passed) or when
|
|
116
|
+
every evaluator was budget-skipped. See feat-006 §4.3 for the
|
|
117
|
+
cost-gating rule.
|
|
118
|
+
"""
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Frozen value types for the `VectorStore` contract.
|
|
2
|
+
|
|
3
|
+
`VectorItem` is what callers upsert into a vector store; `VectorMatch`
|
|
4
|
+
is what searches return. Both are immutable Pydantic models so they
|
|
5
|
+
can be freely passed across async boundaries without aliasing bugs.
|
|
6
|
+
|
|
7
|
+
Per ADR-0007 these shapes are part of the framework's locked surface.
|
|
8
|
+
Adding a field is a minor bump; removing or renaming requires a major
|
|
9
|
+
bump.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class VectorItem(BaseModel):
|
|
20
|
+
"""One indexable record in a `VectorStore`.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
id: Caller-controlled identifier. Re-upserting an existing id
|
|
24
|
+
replaces the prior record (write-through, not append).
|
|
25
|
+
vector: The embedding. Length must match the store's declared
|
|
26
|
+
`dimensions()`; mismatch raises on `upsert`.
|
|
27
|
+
text: The source text the vector was computed from. Surfaced on
|
|
28
|
+
`VectorMatch` so callers don't have to keep a parallel
|
|
29
|
+
text store. Empty strings are allowed but discouraged.
|
|
30
|
+
metadata: Free-form key-value tags. Used by `search`'s
|
|
31
|
+
`filter_metadata=` argument for AND-style filtering. Kept
|
|
32
|
+
as a plain dict (not frozen) so Pydantic doesn't have to
|
|
33
|
+
recurse into arbitrary user payloads.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
37
|
+
|
|
38
|
+
id: str = Field(min_length=1)
|
|
39
|
+
vector: tuple[float, ...]
|
|
40
|
+
text: str
|
|
41
|
+
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class VectorMatch(BaseModel):
|
|
45
|
+
"""One result from `VectorStore.search`.
|
|
46
|
+
|
|
47
|
+
`score` is normalised cosine similarity in `[0, 1]`:
|
|
48
|
+
- 1.0 means identical direction (highest relevance)
|
|
49
|
+
- 0.0 means orthogonal (effectively unrelated)
|
|
50
|
+
- the contract is store-agnostic; drivers that return raw cosine
|
|
51
|
+
distance or negative inner-product convert at the boundary.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
model_config = ConfigDict(frozen=True, strict=True)
|
|
55
|
+
|
|
56
|
+
id: str
|
|
57
|
+
text: str
|
|
58
|
+
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
59
|
+
score: float = Field(ge=0.0, le=1.0)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agentforge-core
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: AgentForge core — stable contracts (ABCs, value types) for the agentic framework
|
|
5
|
+
Project-URL: Homepage, https://github.com/Scaffoldic/agentforge-py
|
|
6
|
+
Project-URL: Repository, https://github.com/Scaffoldic/agentforge-py
|
|
7
|
+
Project-URL: Documentation, https://github.com/Scaffoldic/agentforge-py
|
|
8
|
+
Project-URL: Changelog, https://github.com/Scaffoldic/agentforge-py/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Issues, https://github.com/Scaffoldic/agentforge-py/issues
|
|
10
|
+
Author: The AgentForge Authors
|
|
11
|
+
License-Expression: Apache-2.0
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: agent,ai,contracts,framework,llm
|
|
14
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.13
|
|
22
|
+
Requires-Dist: opentelemetry-api>=1.27
|
|
23
|
+
Requires-Dist: pydantic>=2.10
|
|
24
|
+
Requires-Dist: python-ulid>=3.0
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# agentforge-core
|
|
28
|
+
|
|
29
|
+
The stable contract layer for AgentForge. ABCs, Protocols, and value
|
|
30
|
+
types that every module implements.
|
|
31
|
+
|
|
32
|
+
## What's in here
|
|
33
|
+
|
|
34
|
+
(Once feat-001 lands.)
|
|
35
|
+
|
|
36
|
+
- `LLMClient`, `EmbeddingClient` — provider abstractions
|
|
37
|
+
- `ReasoningStrategy` — agent loop shape
|
|
38
|
+
- `Tool` — pluggable capabilities
|
|
39
|
+
- `MemoryStore`, `GraphStore` — persistence
|
|
40
|
+
- `Evaluator` — post-run scoring
|
|
41
|
+
- `InputValidator`, `OutputValidator`, `ToolCallGate` — real-time safety
|
|
42
|
+
- `Finding` — output Protocol with shipped variants
|
|
43
|
+
- `BudgetPolicy`, `RunContext`, `RunIdFilter` — production rails
|
|
44
|
+
- Value types: `Claim`, `Step`, `RunResult`, etc.
|
|
45
|
+
|
|
46
|
+
This package is a **locked contract** — adding a method to an ABC is a
|
|
47
|
+
major version bump. See ADR-0007.
|
|
48
|
+
|
|
49
|
+
## What's NOT in here
|
|
50
|
+
|
|
51
|
+
- Reference implementations (`ReActLoop`, `InMemoryStore`, etc.) — those
|
|
52
|
+
live in `agentforge`
|
|
53
|
+
- Provider clients — those live in `agentforge-anthropic`, etc.
|
|
54
|
+
- Anything that does I/O
|
|
55
|
+
|
|
56
|
+
## Install
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install agentforge-core
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Most users install `agentforge` directly, which depends on this package.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
Apache 2.0.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
agentforge_core/__init__.py,sha256=McXfkBb7e78HMPMpg415VF_-4Dxu2Fy7rWVfHUj7cvg,5023
|
|
2
|
+
agentforge_core/_bm25.py,sha256=1mjvh7lOBbZPcCYuSFjP6GKJYc7ZpubCeIrpR8Orl04,4632
|
|
3
|
+
agentforge_core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
agentforge_core/config/__init__.py,sha256=wTQGgHGmqLuUpjkpmh044xvxULiifUyX6m326_-hyf8,1690
|
|
5
|
+
agentforge_core/config/loader.py,sha256=NJhKjkLWUc-H0H_f-qXgo0B0ONYlB3NtWh95pHO3DgA,8428
|
|
6
|
+
agentforge_core/config/module_schemas.py,sha256=6PsWBK7gtT-96mopXiHUFNktKR5y-Pv0xTUNv3LbWMk,6746
|
|
7
|
+
agentforge_core/config/schema.py,sha256=uW7OfE2eMPW9nkQ_Cnu3MX9BNDoTLuGkTMvRQo17dF8,15430
|
|
8
|
+
agentforge_core/contracts/__init__.py,sha256=XrbuACw7RI99tcQWkIHZJ2zf0wo3x5qCtEmWJFcgHko,1697
|
|
9
|
+
agentforge_core/contracts/auth.py,sha256=zd-JP-POj7TuXg4ymm7wQDAJHECDh90N3Wp9sONE9C4,1195
|
|
10
|
+
agentforge_core/contracts/chat.py,sha256=VRPZM5PfdFO0hBaESIy9VE8VciOQQlur2_8jQNiLd-8,3873
|
|
11
|
+
agentforge_core/contracts/embedding.py,sha256=tOfiml6NQknmrqO5crR1GXmFHwP5r3jrzwkqYju2x4Q,2582
|
|
12
|
+
agentforge_core/contracts/evaluator.py,sha256=gk0nqtKkXdA-wh6LlEqGwJ7DchYBB5a6yUigd6qvbVs,1842
|
|
13
|
+
agentforge_core/contracts/finding.py,sha256=Z3KuEwuk_07f_yuaHC09C5w9v88eXlZspBsHpiUZK4I,1288
|
|
14
|
+
agentforge_core/contracts/graph_store.py,sha256=XGzRFBzCMyPhTDzXQQTBVOerZqMzFuK9jHFc_nkx1oQ,6441
|
|
15
|
+
agentforge_core/contracts/guardrails.py,sha256=pDOolGTZre0R8sl5_wShcqTy48HxjfC3se8NWGyNxhs,3907
|
|
16
|
+
agentforge_core/contracts/llm.py,sha256=HMxdR3iBcU7ANPNaMo6qecP4XzAH064p3PAYpHXuBdc,5384
|
|
17
|
+
agentforge_core/contracts/memory.py,sha256=VE49No2WYubaUemJNvwNP1Cl7MIy0jDTJzfT_7KGd-g,3861
|
|
18
|
+
agentforge_core/contracts/migrator.py,sha256=gyfQkR2NZu5HjMhQhE4mI2Rz8jkbaGHAQps_b72VuDM,4147
|
|
19
|
+
agentforge_core/contracts/renderer.py,sha256=g3zRlADPhK_wwegyt1rJ5HTMfB3TzMpaSTM3uknUa54,2217
|
|
20
|
+
agentforge_core/contracts/reranker.py,sha256=o3bp7tDGaoEAPaAiq7G3KJ7QzLHirEv162YdCHSKtq0,3404
|
|
21
|
+
agentforge_core/contracts/strategy.py,sha256=zukHQ8yQsgaYWf-13e57YExLM81lq56PN4CQeSsuWcg,2801
|
|
22
|
+
agentforge_core/contracts/task.py,sha256=UGFR48u4yltQbsNJbmgR99FP409ldFO6QIAuT1sZbEs,2501
|
|
23
|
+
agentforge_core/contracts/tool.py,sha256=XUxKt8DvBYqZJICyNWMUaNbt_Oq2cC9hQo148pOUCao,2419
|
|
24
|
+
agentforge_core/contracts/vector_store.py,sha256=tn6s1MBMQrDD7g4xCQnLx69dlKKhuI5nt6BATzmi5ZM,5788
|
|
25
|
+
agentforge_core/migrations/__init__.py,sha256=G2lBRpsMpQehBJMvRGiSMjnoKJ54vRhZ2pqMx78F7uA,525
|
|
26
|
+
agentforge_core/migrations/discover.py,sha256=91QNdJ3I0y5ugKtHvm0BM2KxBYm3ulDeCXl0hFuYJQw,2586
|
|
27
|
+
agentforge_core/migrations/template.py,sha256=REROlDA_BPzcFhXHX3-3esgmBecpyqcXzoWLBzgDW1U,1333
|
|
28
|
+
agentforge_core/observability/__init__.py,sha256=6f4XeuJxVHFMCbP4Z9kBANmDQ3PvKdU8G6GyEFOTK6E,593
|
|
29
|
+
agentforge_core/observability/tracing.py,sha256=tyC6KH8C5zJU4t0ZNsQ1b3tTbCWy4i_rlytSIXgaBos,1260
|
|
30
|
+
agentforge_core/production/__init__.py,sha256=ggvR4qPDMUx1hcRnU2puQcfnLlNx-NoCDXbELkJ7lyQ,2179
|
|
31
|
+
agentforge_core/production/budget.py,sha256=odQMsinrIt5a8RfB7ggrgTEujtxkO13XmCpwU68TM3k,5091
|
|
32
|
+
agentforge_core/production/exceptions.py,sha256=sFeEIC6wHds-Pj_nbro4wX0P8gp2Z_O5dbXXUL2K7qs,4222
|
|
33
|
+
agentforge_core/production/fallback.py,sha256=VXTLcxdlZqd0m_u4mYDKddToXzzRdRf5si5u9aPrZKY,12011
|
|
34
|
+
agentforge_core/production/log_filter.py,sha256=c0zX_UOQQAHusu2k62sfv9Nm9lnAAWgS_TSq3ffffUU,1634
|
|
35
|
+
agentforge_core/production/log_format.py,sha256=ZQ5JBatpYFfGQedFMkryiA4l8ZbA6RT0JlldPVt3hKU,3738
|
|
36
|
+
agentforge_core/production/run_context.py,sha256=gQLRxtBRghOuZ_P_qg24sDCHy8TseNJMBtKoevegr8A,3383
|
|
37
|
+
agentforge_core/resolver/__init__.py,sha256=gXpuZ4cVVmxDviblnPSwh0YFNEOVQyQZwcUa389HVog,1096
|
|
38
|
+
agentforge_core/resolver/discover.py,sha256=w8WaWtt2JHv0HGJmB7XFpx-PuN0ubx3HBC5nqh2aw0E,5045
|
|
39
|
+
agentforge_core/resolver/resolve.py,sha256=iOKV5L7qxgqDOP8kkugih6gXzPSHdAB5WmakqTuzUGI,6234
|
|
40
|
+
agentforge_core/testing/__init__.py,sha256=d3ciS5qI6sjezqiRcX5-FYM0azfGRoy498aESBLEqoQ,1425
|
|
41
|
+
agentforge_core/testing/conformance.py,sha256=_SymngwwBu9Sm5CPwaaBCywkeIC7r9NRedyJ0nbupDg,44951
|
|
42
|
+
agentforge_core/values/__init__.py,sha256=gXQ17KLzotM7TmR7N2mwh_xnAwGImmOv4XChqzot5CI,2192
|
|
43
|
+
agentforge_core/values/auth.py,sha256=dErBR6xagq_wZwhU5x3gD0N1-1Mr4Xtnne-L-IIzLgg,569
|
|
44
|
+
agentforge_core/values/chat.py,sha256=HbIPWoQXYSaHCLny01_j53ozTvG0pEwy5uvCDgun0Cs,4293
|
|
45
|
+
agentforge_core/values/claim.py,sha256=yEd1CW9A6pYj3Htq6an9zk3cWiN0IOjWrE-A4vj594M,900
|
|
46
|
+
agentforge_core/values/graph.py,sha256=WeuClLzIiS3Jo5_CZ30dWObLa0I6-RcgLSTj8sgCs9k,5112
|
|
47
|
+
agentforge_core/values/guardrails.py,sha256=9M-Z6eapS6SD8otbXD9bLoR7yHHOcewWRh0NzeuVr5E,1715
|
|
48
|
+
agentforge_core/values/manifest.py,sha256=rcC1yYgXkBUgq-05OuUoU4wwUb6ohqGPZi8MUBsGmpI,4326
|
|
49
|
+
agentforge_core/values/messages.py,sha256=mNnp7VPS-W0T-Xj2xY38T9hMYhkd9ELeV_ziFaf--Pk,4834
|
|
50
|
+
agentforge_core/values/module.py,sha256=whlYMCx-QwUSitHPv_Ybw95bDWsr9DvanefTDb9qj0s,1614
|
|
51
|
+
agentforge_core/values/pipeline.py,sha256=Fa4Fy73O4wsjSt0oJarIyFRNiNv6dXyEq4QjC-vAsLg,1660
|
|
52
|
+
agentforge_core/values/retrieval.py,sha256=gQt94qmzGz2UI7e07ytR9ITV8xEqthIByMemL7qfowc,1942
|
|
53
|
+
agentforge_core/values/state.py,sha256=9GsRmDONQNxgXnkXF9YoHWEbLXq801cTbpmdEExBf-c,3911
|
|
54
|
+
agentforge_core/values/vector.py,sha256=NCgJ7HZ0cMJ9mewfwV7Bygugu7-5LwhzP5LtSKQCfs8,2122
|
|
55
|
+
agentforge_core-0.2.1.dist-info/METADATA,sha256=fCCvY5CPwETd8oMkmzA9VP3KQoF16822Nzmu6imBI3c,2276
|
|
56
|
+
agentforge_core-0.2.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
57
|
+
agentforge_core-0.2.1.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
58
|
+
agentforge_core-0.2.1.dist-info/RECORD,,
|