mantisdk 0.1.0__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.
Potentially problematic release.
This version of mantisdk might be problematic. Click here for more details.
- mantisdk/__init__.py +22 -0
- mantisdk/adapter/__init__.py +15 -0
- mantisdk/adapter/base.py +94 -0
- mantisdk/adapter/messages.py +270 -0
- mantisdk/adapter/triplet.py +1028 -0
- mantisdk/algorithm/__init__.py +39 -0
- mantisdk/algorithm/apo/__init__.py +5 -0
- mantisdk/algorithm/apo/apo.py +889 -0
- mantisdk/algorithm/apo/prompts/apply_edit_variant01.poml +22 -0
- mantisdk/algorithm/apo/prompts/apply_edit_variant02.poml +18 -0
- mantisdk/algorithm/apo/prompts/text_gradient_variant01.poml +18 -0
- mantisdk/algorithm/apo/prompts/text_gradient_variant02.poml +16 -0
- mantisdk/algorithm/apo/prompts/text_gradient_variant03.poml +107 -0
- mantisdk/algorithm/base.py +162 -0
- mantisdk/algorithm/decorator.py +264 -0
- mantisdk/algorithm/fast.py +250 -0
- mantisdk/algorithm/gepa/__init__.py +59 -0
- mantisdk/algorithm/gepa/adapter.py +459 -0
- mantisdk/algorithm/gepa/gepa.py +364 -0
- mantisdk/algorithm/gepa/lib/__init__.py +18 -0
- mantisdk/algorithm/gepa/lib/adapters/README.md +12 -0
- mantisdk/algorithm/gepa/lib/adapters/__init__.py +0 -0
- mantisdk/algorithm/gepa/lib/adapters/anymaths_adapter/README.md +341 -0
- mantisdk/algorithm/gepa/lib/adapters/anymaths_adapter/__init__.py +1 -0
- mantisdk/algorithm/gepa/lib/adapters/anymaths_adapter/anymaths_adapter.py +174 -0
- mantisdk/algorithm/gepa/lib/adapters/anymaths_adapter/requirements.txt +1 -0
- mantisdk/algorithm/gepa/lib/adapters/default_adapter/README.md +0 -0
- mantisdk/algorithm/gepa/lib/adapters/default_adapter/__init__.py +0 -0
- mantisdk/algorithm/gepa/lib/adapters/default_adapter/default_adapter.py +209 -0
- mantisdk/algorithm/gepa/lib/adapters/dspy_adapter/README.md +7 -0
- mantisdk/algorithm/gepa/lib/adapters/dspy_adapter/__init__.py +0 -0
- mantisdk/algorithm/gepa/lib/adapters/dspy_adapter/dspy_adapter.py +307 -0
- mantisdk/algorithm/gepa/lib/adapters/dspy_full_program_adapter/README.md +99 -0
- mantisdk/algorithm/gepa/lib/adapters/dspy_full_program_adapter/dspy_program_proposal_signature.py +137 -0
- mantisdk/algorithm/gepa/lib/adapters/dspy_full_program_adapter/full_program_adapter.py +266 -0
- mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/GEPA_RAG.md +621 -0
- mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/__init__.py +56 -0
- mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/evaluation_metrics.py +226 -0
- mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/generic_rag_adapter.py +496 -0
- mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/rag_pipeline.py +238 -0
- mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_store_interface.py +212 -0
- mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/__init__.py +2 -0
- mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/chroma_store.py +196 -0
- mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/lancedb_store.py +422 -0
- mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/milvus_store.py +409 -0
- mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/qdrant_store.py +368 -0
- mantisdk/algorithm/gepa/lib/adapters/generic_rag_adapter/vector_stores/weaviate_store.py +418 -0
- mantisdk/algorithm/gepa/lib/adapters/mcp_adapter/README.md +552 -0
- mantisdk/algorithm/gepa/lib/adapters/mcp_adapter/__init__.py +37 -0
- mantisdk/algorithm/gepa/lib/adapters/mcp_adapter/mcp_adapter.py +705 -0
- mantisdk/algorithm/gepa/lib/adapters/mcp_adapter/mcp_client.py +364 -0
- mantisdk/algorithm/gepa/lib/adapters/terminal_bench_adapter/README.md +9 -0
- mantisdk/algorithm/gepa/lib/adapters/terminal_bench_adapter/__init__.py +0 -0
- mantisdk/algorithm/gepa/lib/adapters/terminal_bench_adapter/terminal_bench_adapter.py +217 -0
- mantisdk/algorithm/gepa/lib/api.py +375 -0
- mantisdk/algorithm/gepa/lib/core/__init__.py +0 -0
- mantisdk/algorithm/gepa/lib/core/adapter.py +180 -0
- mantisdk/algorithm/gepa/lib/core/data_loader.py +74 -0
- mantisdk/algorithm/gepa/lib/core/engine.py +356 -0
- mantisdk/algorithm/gepa/lib/core/result.py +233 -0
- mantisdk/algorithm/gepa/lib/core/state.py +636 -0
- mantisdk/algorithm/gepa/lib/examples/__init__.py +0 -0
- mantisdk/algorithm/gepa/lib/examples/aime.py +24 -0
- mantisdk/algorithm/gepa/lib/examples/anymaths-bench/eval_default.py +111 -0
- mantisdk/algorithm/gepa/lib/examples/anymaths-bench/prompt-templates/instruction_prompt.txt +9 -0
- mantisdk/algorithm/gepa/lib/examples/anymaths-bench/prompt-templates/optimal_prompt.txt +24 -0
- mantisdk/algorithm/gepa/lib/examples/anymaths-bench/train_anymaths.py +177 -0
- mantisdk/algorithm/gepa/lib/examples/dspy_full_program_evolution/arc_agi.ipynb +25705 -0
- mantisdk/algorithm/gepa/lib/examples/dspy_full_program_evolution/example.ipynb +348 -0
- mantisdk/algorithm/gepa/lib/examples/mcp_adapter/__init__.py +4 -0
- mantisdk/algorithm/gepa/lib/examples/mcp_adapter/mcp_optimization_example.py +455 -0
- mantisdk/algorithm/gepa/lib/examples/rag_adapter/RAG_GUIDE.md +613 -0
- mantisdk/algorithm/gepa/lib/examples/rag_adapter/__init__.py +9 -0
- mantisdk/algorithm/gepa/lib/examples/rag_adapter/rag_optimization.py +824 -0
- mantisdk/algorithm/gepa/lib/examples/rag_adapter/requirements-rag.txt +29 -0
- mantisdk/algorithm/gepa/lib/examples/terminal-bench/prompt-templates/instruction_prompt.txt +16 -0
- mantisdk/algorithm/gepa/lib/examples/terminal-bench/prompt-templates/terminus.txt +9 -0
- mantisdk/algorithm/gepa/lib/examples/terminal-bench/train_terminus.py +161 -0
- mantisdk/algorithm/gepa/lib/gepa_utils.py +117 -0
- mantisdk/algorithm/gepa/lib/logging/__init__.py +0 -0
- mantisdk/algorithm/gepa/lib/logging/experiment_tracker.py +187 -0
- mantisdk/algorithm/gepa/lib/logging/logger.py +75 -0
- mantisdk/algorithm/gepa/lib/logging/utils.py +103 -0
- mantisdk/algorithm/gepa/lib/proposer/__init__.py +0 -0
- mantisdk/algorithm/gepa/lib/proposer/base.py +31 -0
- mantisdk/algorithm/gepa/lib/proposer/merge.py +357 -0
- mantisdk/algorithm/gepa/lib/proposer/reflective_mutation/__init__.py +0 -0
- mantisdk/algorithm/gepa/lib/proposer/reflective_mutation/base.py +49 -0
- mantisdk/algorithm/gepa/lib/proposer/reflective_mutation/reflective_mutation.py +176 -0
- mantisdk/algorithm/gepa/lib/py.typed +0 -0
- mantisdk/algorithm/gepa/lib/strategies/__init__.py +0 -0
- mantisdk/algorithm/gepa/lib/strategies/batch_sampler.py +77 -0
- mantisdk/algorithm/gepa/lib/strategies/candidate_selector.py +50 -0
- mantisdk/algorithm/gepa/lib/strategies/component_selector.py +36 -0
- mantisdk/algorithm/gepa/lib/strategies/eval_policy.py +64 -0
- mantisdk/algorithm/gepa/lib/strategies/instruction_proposal.py +127 -0
- mantisdk/algorithm/gepa/lib/utils/__init__.py +10 -0
- mantisdk/algorithm/gepa/lib/utils/stop_condition.py +196 -0
- mantisdk/algorithm/gepa/tracing.py +105 -0
- mantisdk/algorithm/utils.py +177 -0
- mantisdk/algorithm/verl/__init__.py +5 -0
- mantisdk/algorithm/verl/interface.py +202 -0
- mantisdk/cli/__init__.py +56 -0
- mantisdk/cli/prometheus.py +115 -0
- mantisdk/cli/store.py +131 -0
- mantisdk/cli/vllm.py +29 -0
- mantisdk/client.py +408 -0
- mantisdk/config.py +348 -0
- mantisdk/emitter/__init__.py +43 -0
- mantisdk/emitter/annotation.py +370 -0
- mantisdk/emitter/exception.py +54 -0
- mantisdk/emitter/message.py +61 -0
- mantisdk/emitter/object.py +117 -0
- mantisdk/emitter/reward.py +320 -0
- mantisdk/env_var.py +156 -0
- mantisdk/execution/__init__.py +15 -0
- mantisdk/execution/base.py +64 -0
- mantisdk/execution/client_server.py +443 -0
- mantisdk/execution/events.py +69 -0
- mantisdk/execution/inter_process.py +16 -0
- mantisdk/execution/shared_memory.py +282 -0
- mantisdk/instrumentation/__init__.py +119 -0
- mantisdk/instrumentation/agentops.py +314 -0
- mantisdk/instrumentation/agentops_langchain.py +45 -0
- mantisdk/instrumentation/litellm.py +83 -0
- mantisdk/instrumentation/vllm.py +81 -0
- mantisdk/instrumentation/weave.py +500 -0
- mantisdk/litagent/__init__.py +11 -0
- mantisdk/litagent/decorator.py +536 -0
- mantisdk/litagent/litagent.py +252 -0
- mantisdk/llm_proxy.py +1890 -0
- mantisdk/logging.py +370 -0
- mantisdk/reward.py +7 -0
- mantisdk/runner/__init__.py +11 -0
- mantisdk/runner/agent.py +845 -0
- mantisdk/runner/base.py +182 -0
- mantisdk/runner/legacy.py +309 -0
- mantisdk/semconv.py +170 -0
- mantisdk/server.py +401 -0
- mantisdk/store/__init__.py +23 -0
- mantisdk/store/base.py +897 -0
- mantisdk/store/client_server.py +2092 -0
- mantisdk/store/collection/__init__.py +30 -0
- mantisdk/store/collection/base.py +587 -0
- mantisdk/store/collection/memory.py +970 -0
- mantisdk/store/collection/mongo.py +1412 -0
- mantisdk/store/collection_based.py +1823 -0
- mantisdk/store/insight.py +648 -0
- mantisdk/store/listener.py +58 -0
- mantisdk/store/memory.py +396 -0
- mantisdk/store/mongo.py +165 -0
- mantisdk/store/sqlite.py +3 -0
- mantisdk/store/threading.py +357 -0
- mantisdk/store/utils.py +142 -0
- mantisdk/tracer/__init__.py +16 -0
- mantisdk/tracer/agentops.py +242 -0
- mantisdk/tracer/base.py +287 -0
- mantisdk/tracer/dummy.py +106 -0
- mantisdk/tracer/otel.py +555 -0
- mantisdk/tracer/weave.py +677 -0
- mantisdk/trainer/__init__.py +6 -0
- mantisdk/trainer/init_utils.py +263 -0
- mantisdk/trainer/legacy.py +367 -0
- mantisdk/trainer/registry.py +12 -0
- mantisdk/trainer/trainer.py +618 -0
- mantisdk/types/__init__.py +6 -0
- mantisdk/types/core.py +553 -0
- mantisdk/types/resources.py +204 -0
- mantisdk/types/tracer.py +515 -0
- mantisdk/types/tracing.py +218 -0
- mantisdk/utils/__init__.py +1 -0
- mantisdk/utils/id.py +18 -0
- mantisdk/utils/metrics.py +1025 -0
- mantisdk/utils/otel.py +578 -0
- mantisdk/utils/otlp.py +536 -0
- mantisdk/utils/server_launcher.py +1045 -0
- mantisdk/utils/system_snapshot.py +81 -0
- mantisdk/verl/__init__.py +8 -0
- mantisdk/verl/__main__.py +6 -0
- mantisdk/verl/async_server.py +46 -0
- mantisdk/verl/config.yaml +27 -0
- mantisdk/verl/daemon.py +1154 -0
- mantisdk/verl/dataset.py +44 -0
- mantisdk/verl/entrypoint.py +248 -0
- mantisdk/verl/trainer.py +549 -0
- mantisdk-0.1.0.dist-info/METADATA +119 -0
- mantisdk-0.1.0.dist-info/RECORD +190 -0
- mantisdk-0.1.0.dist-info/WHEEL +4 -0
- mantisdk-0.1.0.dist-info/entry_points.txt +2 -0
- mantisdk-0.1.0.dist-info/licenses/LICENSE +19 -0
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
# Copyright (c) Microsoft. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Convenience decorators for building lightweight `LitAgent` implementations."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import functools
|
|
8
|
+
import inspect
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any, Awaitable, Callable, Dict, Protocol, TypeGuard, TypeVar, Union, overload
|
|
11
|
+
|
|
12
|
+
from mantisdk.types import (
|
|
13
|
+
LLM,
|
|
14
|
+
AttemptedRollout,
|
|
15
|
+
NamedResources,
|
|
16
|
+
PromptTemplate,
|
|
17
|
+
ProxyLLM,
|
|
18
|
+
Rollout,
|
|
19
|
+
RolloutRawResult,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from .litagent import LitAgent
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
T = TypeVar("T")
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"llm_rollout",
|
|
30
|
+
"prompt_rollout",
|
|
31
|
+
"rollout",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
T_contra = TypeVar("T_contra", contravariant=True)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class LlmRolloutFuncSync2(Protocol[T_contra]):
|
|
39
|
+
def __call__(self, task: T_contra, llm: LLM) -> RolloutRawResult: ...
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class LlmRolloutFuncSync3(Protocol[T_contra]):
|
|
43
|
+
def __call__(self, task: T_contra, llm: LLM, rollout: Rollout) -> RolloutRawResult: ...
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class LlmRolloutFuncAsync2(Protocol[T_contra]):
|
|
47
|
+
def __call__(self, task: T_contra, llm: LLM) -> Awaitable[RolloutRawResult]: ...
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class LlmRolloutFuncAsync3(Protocol[T_contra]):
|
|
51
|
+
def __call__(self, task: T_contra, llm: LLM, rollout: Rollout) -> Awaitable[RolloutRawResult]: ...
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
LlmRolloutFunc = Union[
|
|
55
|
+
LlmRolloutFuncSync2[T_contra],
|
|
56
|
+
LlmRolloutFuncSync3[T_contra],
|
|
57
|
+
LlmRolloutFuncAsync2[T_contra],
|
|
58
|
+
LlmRolloutFuncAsync3[T_contra],
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class PromptRolloutFuncSync2(Protocol[T_contra]):
|
|
63
|
+
def __call__(self, task: T_contra, prompt_template: PromptTemplate) -> RolloutRawResult: ...
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class PromptRolloutFuncAsync2(Protocol[T_contra]):
|
|
67
|
+
def __call__(self, task: T_contra, prompt_template: PromptTemplate) -> Awaitable[RolloutRawResult]: ...
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class PromptRolloutFuncSync3(Protocol[T_contra]):
|
|
71
|
+
def __call__(self, task: T_contra, prompt_template: PromptTemplate, rollout: Rollout) -> RolloutRawResult: ...
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class PromptRolloutFuncAsync3(Protocol[T_contra]):
|
|
75
|
+
def __call__(
|
|
76
|
+
self, task: T_contra, prompt_template: PromptTemplate, rollout: Rollout
|
|
77
|
+
) -> Awaitable[RolloutRawResult]: ...
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
PromptRolloutFunc = Union[
|
|
81
|
+
PromptRolloutFuncSync2[T_contra],
|
|
82
|
+
PromptRolloutFuncSync3[T_contra],
|
|
83
|
+
PromptRolloutFuncAsync2[T_contra],
|
|
84
|
+
PromptRolloutFuncAsync3[T_contra],
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class FunctionalLitAgentFunc(Protocol[T_contra]):
|
|
89
|
+
def __call__(
|
|
90
|
+
self, task: T_contra, *args: Any, **kwargs: Any
|
|
91
|
+
) -> Union[RolloutRawResult, Awaitable[RolloutRawResult]]: ...
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class FunctionalLitAgent(LitAgent[T]):
|
|
95
|
+
"""Adapter that turns plain rollout functions into [`LitAgent`][mantisdk.LitAgent] instances.
|
|
96
|
+
|
|
97
|
+
The helper inspects the wrapped function to determine which resources to
|
|
98
|
+
inject, allowing both synchronous and asynchronous callables to participate
|
|
99
|
+
in the training loop without writing a dedicated subclass.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def __init__(self, rollout_func: FunctionalLitAgentFunc[T], *, strip_proxy: bool = True) -> None:
|
|
103
|
+
"""Initialize the wrapper around a rollout function.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
rollout_func: Callable that implements the rollout. It may be synchronous
|
|
107
|
+
or asynchronous and can optionally receive a
|
|
108
|
+
[`Rollout`][mantisdk.Rollout] alongside resources such as
|
|
109
|
+
`llm` or `prompt_template`.
|
|
110
|
+
strip_proxy: When ``True``, convert
|
|
111
|
+
[`ProxyLLM`][mantisdk.ProxyLLM] inputs into
|
|
112
|
+
[`LLM`][mantisdk.LLM] instances before calling the
|
|
113
|
+
rollout function. Defaults to `True`.
|
|
114
|
+
"""
|
|
115
|
+
super().__init__()
|
|
116
|
+
self._rollout_func = rollout_func
|
|
117
|
+
self._strip_proxy = strip_proxy
|
|
118
|
+
self._is_async = inspect.iscoroutinefunction(rollout_func)
|
|
119
|
+
self._sig = inspect.signature(rollout_func)
|
|
120
|
+
|
|
121
|
+
# Copy function metadata to preserve type hints and other attributes
|
|
122
|
+
functools.update_wrapper(self, rollout_func) # type: ignore
|
|
123
|
+
|
|
124
|
+
def _accepts_rollout(self) -> bool:
|
|
125
|
+
return "rollout" in self._sig.parameters
|
|
126
|
+
|
|
127
|
+
def _accepts_llm(self) -> bool:
|
|
128
|
+
return "llm" in self._sig.parameters
|
|
129
|
+
|
|
130
|
+
def _accepts_prompt_template(self) -> bool:
|
|
131
|
+
return "prompt_template" in self._sig.parameters
|
|
132
|
+
|
|
133
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
|
134
|
+
"""Make the agent instance callable, preserving the original function behavior."""
|
|
135
|
+
return self._rollout_func(*args, **kwargs) # type: ignore
|
|
136
|
+
|
|
137
|
+
def is_async(self) -> bool:
|
|
138
|
+
return self._is_async
|
|
139
|
+
|
|
140
|
+
def rollout(self, task: T, resources: NamedResources, rollout: Rollout) -> RolloutRawResult:
|
|
141
|
+
"""Execute a synchronous rollout using the wrapped function.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
task: Task input data.
|
|
145
|
+
resources: Mapping of named resources available to the agent.
|
|
146
|
+
rollout: Rollout metadata provided by the runtime.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Result produced by the wrapped rollout function.
|
|
150
|
+
|
|
151
|
+
Raises:
|
|
152
|
+
RuntimeError: If the wrapped function is asynchronous.
|
|
153
|
+
"""
|
|
154
|
+
if self._is_async:
|
|
155
|
+
raise RuntimeError(f"{self._rollout_func} is asynchronous. Use rollout_async instead.")
|
|
156
|
+
|
|
157
|
+
kwargs = self._get_kwargs(resources, rollout)
|
|
158
|
+
return self._rollout_func(task, **kwargs) # type: ignore
|
|
159
|
+
|
|
160
|
+
async def rollout_async(self, task: T, resources: NamedResources, rollout: Rollout) -> RolloutRawResult:
|
|
161
|
+
"""Execute an asynchronous rollout using the wrapped function.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
task: Task input data.
|
|
165
|
+
resources: Mapping of named resources available to the agent.
|
|
166
|
+
rollout: Rollout metadata provided by the runtime.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Result produced by the wrapped rollout coroutine.
|
|
170
|
+
|
|
171
|
+
Raises:
|
|
172
|
+
RuntimeError: If the wrapped function is synchronous.
|
|
173
|
+
"""
|
|
174
|
+
if not self._is_async:
|
|
175
|
+
raise RuntimeError(f"{self._rollout_func} is synchronous. Use rollout instead.")
|
|
176
|
+
|
|
177
|
+
kwargs = self._get_kwargs(resources, rollout)
|
|
178
|
+
return await self._rollout_func(task, **kwargs) # type: ignore
|
|
179
|
+
|
|
180
|
+
def _get_kwargs(self, resources: NamedResources, rollout: Rollout) -> Dict[str, Any]:
|
|
181
|
+
"""Prepare keyword arguments expected by the wrapped rollout function.
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
It dynamically builds the `kwargs` dictionary by inspecting the function signature and
|
|
185
|
+
including only the parameters the function accepts. This allows flexible function
|
|
186
|
+
signatures that can request any combination of: rollout, llm, and/or prompt_template.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
resources: Mapping of named resources available for the rollout.
|
|
190
|
+
rollout: Rollout metadata provided by the runtime.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Dictionary of keyword arguments to forward to the rollout function.
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
kwargs: Dict[str, Any] = {}
|
|
197
|
+
if self._accepts_rollout():
|
|
198
|
+
kwargs["rollout"] = rollout
|
|
199
|
+
if self._accepts_llm():
|
|
200
|
+
kwargs["llm"] = self._get_llm_resource(resources, rollout)
|
|
201
|
+
if self._accepts_prompt_template():
|
|
202
|
+
kwargs["prompt_template"] = self._get_prompt_template_resource(resources, rollout)
|
|
203
|
+
|
|
204
|
+
return kwargs
|
|
205
|
+
|
|
206
|
+
def _get_llm_resource(self, resources: NamedResources, rollout: Rollout) -> LLM:
|
|
207
|
+
"""Retrieve the first LLM resource from the available resources.
|
|
208
|
+
|
|
209
|
+
Strip the ProxyLLM resource into a LLM resource if needed.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
resources: Mapping of named resources.
|
|
213
|
+
rollout: Rollout metadata used when stripping proxy endpoints.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
First [`LLM`][mantisdk.LLM] resource encountered.
|
|
217
|
+
|
|
218
|
+
Raises:
|
|
219
|
+
ValueError: If no LLM resource is present.
|
|
220
|
+
"""
|
|
221
|
+
resource_found: LLM | None = None
|
|
222
|
+
for name, resource in resources.items():
|
|
223
|
+
if isinstance(resource, LLM):
|
|
224
|
+
if resource_found is not None:
|
|
225
|
+
logger.warning(f"Multiple LLM resources found in resources. Using the first one: '{name}'.")
|
|
226
|
+
break
|
|
227
|
+
resource_found = resource
|
|
228
|
+
|
|
229
|
+
if resource_found is None:
|
|
230
|
+
raise ValueError("No LLM resource found in the provided resources.")
|
|
231
|
+
|
|
232
|
+
if self._strip_proxy:
|
|
233
|
+
resource_found = self._strip_proxy_helper(resource_found, rollout)
|
|
234
|
+
|
|
235
|
+
return resource_found
|
|
236
|
+
|
|
237
|
+
def _get_prompt_template_resource(self, resources: NamedResources, rollout: Rollout) -> PromptTemplate:
|
|
238
|
+
"""Retrieve the first prompt template resource from the available resources.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
resources: Mapping of named resources.
|
|
242
|
+
rollout: Rollout metadata (unused).
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
First [`PromptTemplate`][mantisdk.PromptTemplate] resource encountered.
|
|
246
|
+
|
|
247
|
+
Raises:
|
|
248
|
+
ValueError: If no prompt template resource is present.
|
|
249
|
+
"""
|
|
250
|
+
resource_found: PromptTemplate | None = None
|
|
251
|
+
for name, resource in resources.items():
|
|
252
|
+
if isinstance(resource, PromptTemplate):
|
|
253
|
+
if resource_found is not None:
|
|
254
|
+
logger.warning(
|
|
255
|
+
f"Multiple prompt template resources found in resources. Using the first one: '{name}'."
|
|
256
|
+
)
|
|
257
|
+
break
|
|
258
|
+
resource_found = resource
|
|
259
|
+
|
|
260
|
+
if resource_found is None:
|
|
261
|
+
raise ValueError("No prompt template resource found in the provided resources.")
|
|
262
|
+
|
|
263
|
+
return resource_found
|
|
264
|
+
|
|
265
|
+
def _strip_proxy_helper(self, proxy_llm: LLM, rollout: Rollout) -> LLM:
|
|
266
|
+
"""Convert [`ProxyLLM`][mantisdk.ProxyLLM] instances into concrete LLMs.
|
|
267
|
+
|
|
268
|
+
It resolves ProxyLLM instances to their concrete LLM implementation
|
|
269
|
+
by attaching the attempted rollout context. This is only used when the function
|
|
270
|
+
signature accepts an `llm` parameter and strip_proxy is True.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
proxy_llm: Candidate LLM resource.
|
|
274
|
+
rollout: Rollout metadata that provides rollout and attempt identifiers.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
[`LLM`][mantisdk.LLM] with rollout context baked into the endpoint.
|
|
278
|
+
|
|
279
|
+
Raises:
|
|
280
|
+
ValueError: If the rollout is not an
|
|
281
|
+
[`AttemptedRollout`][mantisdk.AttemptedRollout].
|
|
282
|
+
"""
|
|
283
|
+
|
|
284
|
+
if not isinstance(proxy_llm, ProxyLLM):
|
|
285
|
+
# Not a ProxyLLM, nothing to strip here.
|
|
286
|
+
return proxy_llm
|
|
287
|
+
|
|
288
|
+
# Rollout is still a Rollout here because API is not stabilized yet.
|
|
289
|
+
# In practice, it must be an AttemptedRollout.
|
|
290
|
+
if not isinstance(rollout, AttemptedRollout):
|
|
291
|
+
raise ValueError("Rollout is not an AttemptedRollout.")
|
|
292
|
+
|
|
293
|
+
return proxy_llm.with_attempted_rollout(rollout)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
@overload
|
|
297
|
+
def llm_rollout(func: LlmRolloutFunc[T]) -> FunctionalLitAgent[T]: ...
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@overload
|
|
301
|
+
def llm_rollout(*, strip_proxy: bool = True) -> Callable[[LlmRolloutFunc[T]], FunctionalLitAgent[T]]: ...
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def llm_rollout(
|
|
305
|
+
func: LlmRolloutFunc[T] | None = None, *, strip_proxy: bool = True
|
|
306
|
+
) -> FunctionalLitAgent[T] | Callable[[LlmRolloutFunc[T]], FunctionalLitAgent[T]]:
|
|
307
|
+
"""Create a [`FunctionalLitAgent`][mantisdk.litagent.decorator.FunctionalLitAgent] for LLM-based rollouts.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
func: Callable defining the agent's behaviour. Supported signatures include:
|
|
311
|
+
|
|
312
|
+
* `(task, llm) -> result`
|
|
313
|
+
* `(task, llm, rollout) -> result`
|
|
314
|
+
* `async (task, llm) -> result`
|
|
315
|
+
* `async (task, llm, rollout) -> result`
|
|
316
|
+
|
|
317
|
+
strip_proxy: When `True`, convert proxy resources into concrete
|
|
318
|
+
[`LLM`][mantisdk.LLM] instances before calling the
|
|
319
|
+
function. Defaults to `True`.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
[`FunctionalLitAgent`][mantisdk.litagent.decorator.FunctionalLitAgent] that
|
|
323
|
+
wraps the supplied function.
|
|
324
|
+
|
|
325
|
+
Examples:
|
|
326
|
+
```python
|
|
327
|
+
@llm_rollout
|
|
328
|
+
def my_agent(task, llm):
|
|
329
|
+
return llm.endpoint
|
|
330
|
+
|
|
331
|
+
@llm_rollout(strip_proxy=False)
|
|
332
|
+
def my_agent_no_strip(task, llm):
|
|
333
|
+
return llm.model
|
|
334
|
+
|
|
335
|
+
result = my_agent(task, llm)
|
|
336
|
+
result = my_agent.rollout(task, resources, rollout)
|
|
337
|
+
```
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
def decorator(f: LlmRolloutFunc[T]) -> FunctionalLitAgent[T]:
|
|
341
|
+
_validate_llm_rollout_func(f)
|
|
342
|
+
return FunctionalLitAgent(f, strip_proxy=strip_proxy)
|
|
343
|
+
|
|
344
|
+
if func is None:
|
|
345
|
+
# Called with arguments: @llm_rollout(strip_proxy=False)
|
|
346
|
+
return decorator
|
|
347
|
+
else:
|
|
348
|
+
# Called without arguments: @llm_rollout
|
|
349
|
+
return decorator(func)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _validate_llm_rollout_func(func: Any) -> TypeGuard[LlmRolloutFunc[Any]]:
|
|
353
|
+
"""Validate the function signature of an LLM rollout function.
|
|
354
|
+
|
|
355
|
+
Ensures the function follows the expected pattern for LLM-based rollouts:
|
|
356
|
+
|
|
357
|
+
- Must have at least 2 parameters
|
|
358
|
+
- First parameter must be named 'task'
|
|
359
|
+
- Must have a parameter named 'llm'
|
|
360
|
+
- Optionally can have a 'rollout' parameter
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
func: Function to inspect.
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
`True` when the signature matches the supported patterns.
|
|
367
|
+
|
|
368
|
+
Raises:
|
|
369
|
+
ValueError: If the function signature does not match the expected pattern.
|
|
370
|
+
"""
|
|
371
|
+
sig = inspect.signature(func)
|
|
372
|
+
params = list(sig.parameters.keys())
|
|
373
|
+
if len(params) < 2:
|
|
374
|
+
raise ValueError(f"Function {func} must have at least 2 parameters.")
|
|
375
|
+
if params[0] != "task":
|
|
376
|
+
raise ValueError(f"Function {func} must be a positional parameter called 'task'.")
|
|
377
|
+
if "llm" not in params:
|
|
378
|
+
raise ValueError(f"Function {func} must have a positional parameter called 'llm'.")
|
|
379
|
+
|
|
380
|
+
return True
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
@overload
|
|
384
|
+
def prompt_rollout(func: PromptRolloutFunc[T]) -> FunctionalLitAgent[T]: ...
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
@overload
|
|
388
|
+
def prompt_rollout() -> Callable[[PromptRolloutFunc[T]], FunctionalLitAgent[T]]: ...
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def prompt_rollout(
|
|
392
|
+
func: PromptRolloutFunc[T] | None = None,
|
|
393
|
+
) -> FunctionalLitAgent[T] | Callable[[PromptRolloutFunc[T]], FunctionalLitAgent[T]]:
|
|
394
|
+
"""Create a [`FunctionalLitAgent`][mantisdk.litagent.decorator.FunctionalLitAgent] for prompt-based rollouts.
|
|
395
|
+
|
|
396
|
+
This decorator is designed for agents that work with tunable prompt templates. It enables
|
|
397
|
+
a workflow where algorithms manage and optimize the prompt template, while agents consume
|
|
398
|
+
the template to perform rollouts. This is particularly useful for prompt optimization scenarios.
|
|
399
|
+
|
|
400
|
+
Args:
|
|
401
|
+
func: Callable defining the agent's behavior. Supported signatures include:
|
|
402
|
+
|
|
403
|
+
* `(task, prompt_template) -> result`
|
|
404
|
+
* `(task, prompt_template, rollout) -> result`
|
|
405
|
+
* `async (task, prompt_template) -> result`
|
|
406
|
+
* `async (task, prompt_template, rollout) -> result`
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
[`FunctionalLitAgent`][mantisdk.litagent.decorator.FunctionalLitAgent] that
|
|
410
|
+
wraps the supplied function.
|
|
411
|
+
|
|
412
|
+
Examples:
|
|
413
|
+
```python
|
|
414
|
+
@prompt_rollout
|
|
415
|
+
def my_agent(task, prompt_template):
|
|
416
|
+
messages = prompt_template.format(task=task.input)
|
|
417
|
+
return messages
|
|
418
|
+
|
|
419
|
+
result = my_agent(task, prompt_template)
|
|
420
|
+
result = my_agent.rollout(task, resources, rollout)
|
|
421
|
+
```
|
|
422
|
+
"""
|
|
423
|
+
|
|
424
|
+
def decorator(f: PromptRolloutFunc[T]) -> FunctionalLitAgent[T]:
|
|
425
|
+
_validate_prompt_rollout_func(f)
|
|
426
|
+
return FunctionalLitAgent(f)
|
|
427
|
+
|
|
428
|
+
if func is None:
|
|
429
|
+
return decorator
|
|
430
|
+
else:
|
|
431
|
+
return decorator(func)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def _validate_prompt_rollout_func(func: Any) -> TypeGuard[PromptRolloutFunc[Any]]:
|
|
435
|
+
"""Validate the function signature of a prompt rollout function.
|
|
436
|
+
|
|
437
|
+
Ensures the function follows the expected pattern for prompt-template-based rollouts:
|
|
438
|
+
|
|
439
|
+
- Must have at least 2 parameters
|
|
440
|
+
- First parameter must be named 'task'
|
|
441
|
+
- Must have a parameter named 'prompt_template'
|
|
442
|
+
- Optionally can have a 'rollout' parameter
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
func: Function to inspect.
|
|
446
|
+
|
|
447
|
+
Returns:
|
|
448
|
+
`True` when the signature matches the supported patterns.
|
|
449
|
+
|
|
450
|
+
Raises:
|
|
451
|
+
ValueError: If the function signature does not match the expected pattern.
|
|
452
|
+
"""
|
|
453
|
+
sig = inspect.signature(func)
|
|
454
|
+
params = list(sig.parameters.keys())
|
|
455
|
+
if len(params) < 2:
|
|
456
|
+
raise ValueError(f"Function {func} must have at least 2 parameters.")
|
|
457
|
+
if params[0] != "task":
|
|
458
|
+
raise ValueError(f"Function {func} must be a positional parameter called 'task'.")
|
|
459
|
+
if "prompt_template" not in params:
|
|
460
|
+
raise ValueError(f"Function {func} must have a positional parameter called 'prompt_template'.")
|
|
461
|
+
|
|
462
|
+
return True
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def rollout(func: Union[LlmRolloutFunc[T], PromptRolloutFunc[T], Callable[..., Any]]) -> FunctionalLitAgent[T]:
|
|
466
|
+
"""Create a [`FunctionalLitAgent`][mantisdk.litagent.decorator.FunctionalLitAgent] from an arbitrary rollout function.
|
|
467
|
+
|
|
468
|
+
This function inspects the provided callable and creates the appropriate
|
|
469
|
+
agent type based on its signature. It supports both LLM-based and prompt-template-based
|
|
470
|
+
agents. The returned agent instance is callable, preserving the original function's
|
|
471
|
+
behavior and type hints.
|
|
472
|
+
|
|
473
|
+
See [`llm_rollout`][mantisdk.litagent.decorator.llm_rollout] and
|
|
474
|
+
[`prompt_rollout`][mantisdk.litagent.decorator.prompt_rollout] for more details.
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
func: Callable that implements the rollout. Supported signatures:
|
|
478
|
+
|
|
479
|
+
- `[async ](task, llm[, rollout])` for LLM-based agents
|
|
480
|
+
- `[async ](task, prompt_template[, rollout])` for prompt-template-based agents
|
|
481
|
+
|
|
482
|
+
The supported output types of `func` is same as the return type of [`rollout`][mantisdk.LitAgent.rollout].
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
[`FunctionalLitAgent`][mantisdk.litagent.decorator.FunctionalLitAgent] that
|
|
486
|
+
wraps the supplied function.
|
|
487
|
+
|
|
488
|
+
Examples:
|
|
489
|
+
```python
|
|
490
|
+
# LLM-based agent
|
|
491
|
+
@rollout
|
|
492
|
+
def my_llm_agent(task, llm):
|
|
493
|
+
client = OpenAI(base_url=llm.endpoint)
|
|
494
|
+
response = client.chat.completions.create(
|
|
495
|
+
model=llm.model,
|
|
496
|
+
messages=[{"role": "user", "content": task.input}],
|
|
497
|
+
)
|
|
498
|
+
return response
|
|
499
|
+
|
|
500
|
+
# Prompt-template-based agent
|
|
501
|
+
@rollout
|
|
502
|
+
def my_prompt_agent(task, prompt_template):
|
|
503
|
+
messages = prompt_template.format(task=task.input)
|
|
504
|
+
# ... perform rollout with the formatted prompt
|
|
505
|
+
return response
|
|
506
|
+
|
|
507
|
+
# Function is still callable with original behavior
|
|
508
|
+
result = my_llm_agent(task, llm)
|
|
509
|
+
|
|
510
|
+
# Agent methods are also available
|
|
511
|
+
result = my_llm_agent.rollout(task, resources, rollout)
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
Raises:
|
|
515
|
+
NotImplementedError: If the function signature doesn't match any known patterns.
|
|
516
|
+
"""
|
|
517
|
+
# Check if it matches the LLM rollout API pattern
|
|
518
|
+
sig = inspect.signature(func)
|
|
519
|
+
|
|
520
|
+
try:
|
|
521
|
+
if _validate_llm_rollout_func(func):
|
|
522
|
+
return llm_rollout(func)
|
|
523
|
+
except ValueError:
|
|
524
|
+
pass
|
|
525
|
+
|
|
526
|
+
try:
|
|
527
|
+
if _validate_prompt_rollout_func(func):
|
|
528
|
+
return prompt_rollout(func)
|
|
529
|
+
except ValueError:
|
|
530
|
+
pass
|
|
531
|
+
|
|
532
|
+
raise NotImplementedError(
|
|
533
|
+
f"Function signature {sig} does not match any known agent patterns. "
|
|
534
|
+
"Expected signatures: (task, llm[, rollout]) or (task, prompt_template[, rollout]). "
|
|
535
|
+
"Functions can be sync or async."
|
|
536
|
+
)
|