flyteplugins-agents-mistral 2.5.9__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.
- flyteplugins/agents/mistral/__init__.py +16 -0
- flyteplugins/agents/mistral/_run.py +265 -0
- flyteplugins_agents_mistral-2.5.9.dist-info/METADATA +103 -0
- flyteplugins_agents_mistral-2.5.9.dist-info/RECORD +6 -0
- flyteplugins_agents_mistral-2.5.9.dist-info/WHEEL +5 -0
- flyteplugins_agents_mistral-2.5.9.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Mistral Agents adapter for Flyte (mistralai 2.x).
|
|
2
|
+
|
|
3
|
+
Bring your own Mistral agent and run it durably on Flyte. Tools you
|
|
4
|
+
expose are Flyte tasks (each call a durable child action), and each model turn is
|
|
5
|
+
recorded via ``flyte.trace`` (the turns are in-process HTTP calls, so we can trace
|
|
6
|
+
the seam below the SDK's loop) for per-turn replay on retry.
|
|
7
|
+
|
|
8
|
+
- :func:`tool` — turn an ``@env.task`` into a Mistral run-framework tool.
|
|
9
|
+
- :func:`run_agent` — run the SDK's agent loop inside your task; return the answer.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from flyteplugins.agents.core import tool
|
|
13
|
+
|
|
14
|
+
from ._run import run_agent
|
|
15
|
+
|
|
16
|
+
__all__ = ["run_agent", "tool"]
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""``run_agent`` — run a Mistral agent.
|
|
2
|
+
|
|
3
|
+
Durability via the seam below the loop: ``run_async`` makes each model turn by
|
|
4
|
+
calling ``self.start_async``/``self.append_async`` on the conversations object.
|
|
5
|
+
When ``durable=True`` we wrap those two methods so each turn is recorded via
|
|
6
|
+
``durable_step`` (a ``flyte.trace`` leaf) — so a crash/retry replays completed
|
|
7
|
+
turns from their recorded ``ConversationResponse`` and completed tool calls are
|
|
8
|
+
cache hits.
|
|
9
|
+
|
|
10
|
+
The same seam also drives observability: the SDK's ``RunResult`` exposes no token
|
|
11
|
+
usage, but each turn's ``ConversationResponse`` does — so the wrapper tallies the
|
|
12
|
+
model-turn count + token usage and we render it as a summary row in the report. On a
|
|
13
|
+
retry, turns served from their durable record are counted as cached tokens so the row
|
|
14
|
+
shows they weren't re-billed (rather than passing a free replay off as fresh spend).
|
|
15
|
+
|
|
16
|
+
The API key is read from the environment — wire it as a Flyte secret.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import typing
|
|
23
|
+
|
|
24
|
+
from flyte._logging import logger
|
|
25
|
+
from flyte._task import AsyncFunctionTaskTemplate
|
|
26
|
+
from flyteplugins.agents.core import (
|
|
27
|
+
ReportTimeline,
|
|
28
|
+
abbrev,
|
|
29
|
+
durable_step,
|
|
30
|
+
fingerprint,
|
|
31
|
+
flush_report,
|
|
32
|
+
jsonable,
|
|
33
|
+
resolve_memory,
|
|
34
|
+
tool,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Semantic fields of a start/append call used to key the durable turn.
|
|
38
|
+
_TURN_KEY_FIELDS = ("inputs", "model", "agent_id", "instructions", "tools", "conversation_id")
|
|
39
|
+
|
|
40
|
+
# Path-addressed memory slot holding a thread's server-side conversation id.
|
|
41
|
+
_MEMORY_CONV_PATH = "mistral/conversation_id"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _coerce_tool(t: typing.Any) -> typing.Callable:
|
|
45
|
+
return tool(t) if isinstance(t, AsyncFunctionTaskTemplate) else t
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _entry_type(entry: typing.Any) -> str | None:
|
|
49
|
+
return getattr(entry, "type", None)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _entry_text(entry: typing.Any) -> str:
|
|
53
|
+
content = getattr(entry, "content", "")
|
|
54
|
+
if isinstance(content, str):
|
|
55
|
+
return content
|
|
56
|
+
if isinstance(content, list):
|
|
57
|
+
return "".join(c if isinstance(c, str) else (getattr(c, "text", "") or "") for c in content)
|
|
58
|
+
return getattr(content, "text", "") or ""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _final_text(output_entries: list[typing.Any]) -> str:
|
|
62
|
+
return "".join(_entry_text(e) for e in output_entries if _entry_type(e) == "message.output")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _render(timeline: ReportTimeline, output_entries: list[typing.Any]) -> None:
|
|
66
|
+
for entry in output_entries:
|
|
67
|
+
kind = _entry_type(entry)
|
|
68
|
+
if kind == "function.call":
|
|
69
|
+
timeline.row(
|
|
70
|
+
icon="🛠️",
|
|
71
|
+
label=getattr(entry, "name", ""),
|
|
72
|
+
meta="tool",
|
|
73
|
+
detail=abbrev(getattr(entry, "arguments", ""), 160),
|
|
74
|
+
)
|
|
75
|
+
elif kind == "message.output":
|
|
76
|
+
timeline.row(icon="💬", label="assistant", detail=abbrev(_entry_text(entry), 200))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class _UsageSink:
|
|
80
|
+
"""Tally model-turn count + token usage across the SDK's loop.
|
|
81
|
+
|
|
82
|
+
``RunResult`` surfaces no usage, but each model turn's ``ConversationResponse``
|
|
83
|
+
carries ``.usage`` — and every turn flows through our start/append wrapper, so we
|
|
84
|
+
tally it there. Tokens for a turn served from a durable record on a retry (no real
|
|
85
|
+
model call) are counted as ``cached``, so the row shows they weren't re-billed.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def __init__(self) -> None:
|
|
89
|
+
self.turns = self.prompt = self.completion = self.total = self.connector = self.cached = 0
|
|
90
|
+
|
|
91
|
+
def add(self, response: typing.Any, *, cached: bool = False) -> None:
|
|
92
|
+
self.turns += 1
|
|
93
|
+
usage = getattr(response, "usage", None)
|
|
94
|
+
turn_total = (getattr(usage, "total_tokens", 0) or 0) if usage is not None else 0
|
|
95
|
+
if usage is not None:
|
|
96
|
+
self.prompt += getattr(usage, "prompt_tokens", 0) or 0
|
|
97
|
+
self.completion += getattr(usage, "completion_tokens", 0) or 0
|
|
98
|
+
self.total += turn_total
|
|
99
|
+
self.connector += getattr(usage, "connector_tokens", 0) or 0
|
|
100
|
+
if cached:
|
|
101
|
+
self.cached += turn_total
|
|
102
|
+
|
|
103
|
+
def detail(self) -> str:
|
|
104
|
+
out = (
|
|
105
|
+
f"{self.turns} model turns · {self.prompt} prompt · "
|
|
106
|
+
f"{self.completion} completion · {self.total} total tokens"
|
|
107
|
+
)
|
|
108
|
+
if self.connector:
|
|
109
|
+
out += f" · {self.connector} connector"
|
|
110
|
+
if self.cached:
|
|
111
|
+
out += f" · {self.cached} cached"
|
|
112
|
+
return out
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _install_turn_hooks(conversations: typing.Any, *, durable: bool, usage: _UsageSink | None) -> None:
|
|
116
|
+
"""Wrap ``run_async``'s internal ``start``/``append`` for durability + usage.
|
|
117
|
+
|
|
118
|
+
``run_async`` calls ``self.start_async``/``self.append_async`` for each model
|
|
119
|
+
turn, so shadowing those instance methods lets us (a) record/replay each turn via
|
|
120
|
+
``durable_step`` (the seam below the SDK's loop) when ``durable``, and (b) tally
|
|
121
|
+
tokens from each turn's ``ConversationResponse`` when ``usage`` is given — neither
|
|
122
|
+
of which the SDK's ``RunResult`` exposes. The response round-trips through pydantic
|
|
123
|
+
JSON (verified faithful, incl. the polymorphic outputs).
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
from mistralai.client.models import ConversationResponse
|
|
127
|
+
|
|
128
|
+
original = {"start": conversations.start_async, "append": conversations.append_async}
|
|
129
|
+
|
|
130
|
+
def _wrap(phase: str):
|
|
131
|
+
orig = original[phase]
|
|
132
|
+
|
|
133
|
+
async def _turn(**kwargs: typing.Any) -> typing.Any:
|
|
134
|
+
cached = False
|
|
135
|
+
if durable:
|
|
136
|
+
# ``durable_step`` invokes ``run`` only on a fresh execution; on a retry
|
|
137
|
+
# it returns the recorded result without calling it. Flip a flag inside
|
|
138
|
+
# the closure so we know per-turn whether the model was actually called
|
|
139
|
+
# (fresh) or the turn came from its record (counted as cached, not re-billed).
|
|
140
|
+
fired = False
|
|
141
|
+
|
|
142
|
+
async def _do() -> typing.Any:
|
|
143
|
+
nonlocal fired
|
|
144
|
+
fired = True
|
|
145
|
+
return await orig(**kwargs)
|
|
146
|
+
|
|
147
|
+
key = fingerprint(
|
|
148
|
+
{
|
|
149
|
+
"phase": phase,
|
|
150
|
+
**{k: jsonable(kwargs[k]) for k in _TURN_KEY_FIELDS if kwargs.get(k) is not None},
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
response = await durable_step(
|
|
154
|
+
key,
|
|
155
|
+
_do,
|
|
156
|
+
name=f"conversation_{phase}",
|
|
157
|
+
dumps=lambda r: r.model_dump_json(),
|
|
158
|
+
loads=ConversationResponse.model_validate_json,
|
|
159
|
+
)
|
|
160
|
+
cached = not fired
|
|
161
|
+
else:
|
|
162
|
+
response = await orig(**kwargs)
|
|
163
|
+
if usage is not None:
|
|
164
|
+
usage.add(response, cached=cached)
|
|
165
|
+
return response
|
|
166
|
+
|
|
167
|
+
return _turn
|
|
168
|
+
|
|
169
|
+
conversations.start_async = _wrap("start")
|
|
170
|
+
conversations.append_async = _wrap("append")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
async def run_agent(
|
|
174
|
+
input: str,
|
|
175
|
+
*,
|
|
176
|
+
tools: typing.Sequence[typing.Any] = (),
|
|
177
|
+
model: str | None = "mistral-large-latest",
|
|
178
|
+
instructions: str | None = None,
|
|
179
|
+
timeout_ms: int | None = None,
|
|
180
|
+
durable: bool = True,
|
|
181
|
+
observability: bool = True,
|
|
182
|
+
agent_id: str | None = None,
|
|
183
|
+
api_key_env_var: str = "MISTRAL_API_KEY",
|
|
184
|
+
memory_key: str | None = None,
|
|
185
|
+
) -> str:
|
|
186
|
+
"""Run a Mistral agent with the given tools and prompt; return the final text.
|
|
187
|
+
|
|
188
|
+
Call this from inside an ``@env.task`` — that task is the durable parent.
|
|
189
|
+
The Mistral SDK runs the agent loop; each tool the agent calls runs as a
|
|
190
|
+
durable Flyte child action, and (with ``durable=True``) each model turn is
|
|
191
|
+
recorded for replay. Pass ``agent_id`` to drive a pre-created server-side
|
|
192
|
+
agent instead of an inline ``model``.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
input: The user prompt.
|
|
196
|
+
tools: ``tool``-wrapped tools or bare ``@env.task`` templates.
|
|
197
|
+
model: Model for an inline run (when ``agent_id`` is not given).
|
|
198
|
+
instructions: System instructions.
|
|
199
|
+
timeout_ms: Per-turn request timeout (ms), applied by the SDK to each model
|
|
200
|
+
call inside its loop; ``None`` uses the SDK default. This bounds a single
|
|
201
|
+
hung turn — it is not a whole-run cap (Mistral exposes no turn-count
|
|
202
|
+
limit). To bound the entire agent run, set ``timeout=`` on the enclosing
|
|
203
|
+
``@env.task`` (the durable parent), which caps all turns + tool calls.
|
|
204
|
+
durable: Record/replay each conversation turn via ``flyte.trace``.
|
|
205
|
+
observability: Render the run timeline into the Flyte task report.
|
|
206
|
+
agent_id: Reuse an existing server-side agent (instead of ``model``).
|
|
207
|
+
api_key_env_var: Env var holding the Mistral API key (wire as a secret).
|
|
208
|
+
memory_key: Stable id (e.g. a user/thread id) for cross-run memory.
|
|
209
|
+
When set, the thread's server-side ``conversation_id`` is persisted in a
|
|
210
|
+
keyed ``MemoryStore`` and reused, so a later run with the same key
|
|
211
|
+
continues the conversation. ``None`` disables memory.
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
from mistralai.client import Mistral
|
|
215
|
+
from mistralai.extra.run.context import RunContext
|
|
216
|
+
|
|
217
|
+
api_key = os.environ.get(api_key_env_var)
|
|
218
|
+
if not api_key:
|
|
219
|
+
raise ValueError(
|
|
220
|
+
f"Mistral API key not found in '{api_key_env_var}'. Provide it as a Flyte secret, e.g. "
|
|
221
|
+
f'flyte.Secret(key="mistral_api_key", as_env_var="{api_key_env_var}").'
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
client = Mistral(api_key=api_key)
|
|
225
|
+
conversations = client.beta.conversations
|
|
226
|
+
|
|
227
|
+
timeline = ReportTimeline() if observability else None
|
|
228
|
+
usage = _UsageSink() if observability else None
|
|
229
|
+
if timeline is not None:
|
|
230
|
+
timeline.heading("Mistral agent")
|
|
231
|
+
|
|
232
|
+
# Wrap the per-turn seam for durable replay (``durable``) and/or token tallying
|
|
233
|
+
# (``usage``) — the SDK's ``RunResult`` surfaces neither, but every turn flows here.
|
|
234
|
+
if durable or usage is not None:
|
|
235
|
+
try:
|
|
236
|
+
_install_turn_hooks(conversations, durable=durable, usage=usage)
|
|
237
|
+
except Exception: # pragma: no cover - never break the run over hook wiring
|
|
238
|
+
logger.warning("Could not install conversation-turn hooks; continuing without replay/usage.")
|
|
239
|
+
|
|
240
|
+
# Cross-run memory: Mistral keeps the transcript server-side, so we persist just
|
|
241
|
+
# the conversation id and continue that conversation when the key recurs.
|
|
242
|
+
store = await resolve_memory(memory_key)
|
|
243
|
+
conversation_id = await store.read_json.aio(_MEMORY_CONV_PATH) if store is not None else None
|
|
244
|
+
|
|
245
|
+
run_ctx_kwargs = {"agent_id": agent_id} if agent_id is not None else {"model": model}
|
|
246
|
+
if conversation_id:
|
|
247
|
+
run_ctx_kwargs["conversation_id"] = conversation_id
|
|
248
|
+
|
|
249
|
+
async with RunContext(**run_ctx_kwargs) as run_ctx:
|
|
250
|
+
for fn in (_coerce_tool(t) for t in tools):
|
|
251
|
+
run_ctx.register_func(fn)
|
|
252
|
+
|
|
253
|
+
result = await conversations.run_async(run_ctx, inputs=input, instructions=instructions, timeout_ms=timeout_ms)
|
|
254
|
+
|
|
255
|
+
if store is not None and result.conversation_id:
|
|
256
|
+
await store.write_json.aio(_MEMORY_CONV_PATH, result.conversation_id, actor="mistral-agent")
|
|
257
|
+
await store.save.aio()
|
|
258
|
+
|
|
259
|
+
output_entries = list(result.output_entries or [])
|
|
260
|
+
if timeline is not None:
|
|
261
|
+
_render(timeline, output_entries)
|
|
262
|
+
if usage is not None and usage.turns:
|
|
263
|
+
timeline.row(icon="📊", label="usage", meta="model", detail=usage.detail())
|
|
264
|
+
await flush_report()
|
|
265
|
+
return _final_text(output_entries)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flyteplugins-agents-mistral
|
|
3
|
+
Version: 2.5.9
|
|
4
|
+
Summary: Run Mistral Agents (Conversations API) on Flyte.
|
|
5
|
+
Author-email: Samhita Alla <samhita@union.ai>
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: flyteplugins-agents-core
|
|
9
|
+
Requires-Dist: mistralai[agents]>=2.0
|
|
10
|
+
|
|
11
|
+
# flyteplugins-agents-mistral
|
|
12
|
+
|
|
13
|
+
Run [Mistral Agents](https://docs.mistral.ai/agents/agents_introduction/) (the
|
|
14
|
+
Conversations API, `mistralai` 2.x) on Flyte. You keep writing Mistral agents;
|
|
15
|
+
Flyte is the runtime underneath.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install flyteplugins-agents-mistral
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import flyte
|
|
23
|
+
from flyteplugins.agents.mistral import tool, run_agent
|
|
24
|
+
|
|
25
|
+
env = flyte.TaskEnvironment(
|
|
26
|
+
"mistral-agent",
|
|
27
|
+
secrets=[flyte.Secret(key="mistral_api_key", as_env_var="MISTRAL_API_KEY")],
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
@tool
|
|
31
|
+
@env.task(cache="auto", retries=3)
|
|
32
|
+
async def get_weather(city: str) -> str:
|
|
33
|
+
"""Get the current weather for a city."""
|
|
34
|
+
return f"The weather in {city} is sunny, 22°C."
|
|
35
|
+
|
|
36
|
+
@env.task(report=True, retries=3)
|
|
37
|
+
async def city_agent(question: str) -> str:
|
|
38
|
+
return await run_agent(question, tools=[get_weather], model="mistral-large-latest")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## How it maps to Flyte
|
|
42
|
+
|
|
43
|
+
- The SDK owns the loop — we don't reimplement it. Mistral's own runner
|
|
44
|
+
(`conversations.run_async` + `RunContext`) drives the agent loop and executes
|
|
45
|
+
the tools; we just register Flyte-task-backed tools with it. `tool`
|
|
46
|
+
produces the Python function the runner calls, and its body dispatches to
|
|
47
|
+
`task.aio()`, so each tool call is a durable Flyte child action.
|
|
48
|
+
- Both per-turn and per-tool durability. The runner makes each model turn by
|
|
49
|
+
calling `start_async`/`append_async` (in-process HTTP), so with `durable=True`
|
|
50
|
+
we wrap those two methods — the seam below the loop — and record each turn
|
|
51
|
+
via `flyte.trace` (the `ConversationResponse` round-trips through pydantic JSON).
|
|
52
|
+
On a crash/retry, completed turns replay and completed tool calls are cache
|
|
53
|
+
hits — all while the SDK still owns the loop. (Same idea as swapping OpenAI's
|
|
54
|
+
`ModelProvider`: trace the model-call seam, not the loop.)
|
|
55
|
+
- Observability: the turns, tool calls and final answer render into the task
|
|
56
|
+
report.
|
|
57
|
+
|
|
58
|
+
The API key is read from the environment. Wire it as a Flyte secret.
|
|
59
|
+
|
|
60
|
+
## Memory
|
|
61
|
+
|
|
62
|
+
Pass `memory_key` (a user/thread id) for cross-run memory — the agent continues
|
|
63
|
+
the same conversation across separate runs:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
await run_agent(message, model="mistral-large-latest", memory_key="user-alice")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Mistral keeps the transcript server-side, so Flyte durably persists the thread's
|
|
70
|
+
`conversation_id` (in a keyed `MemoryStore`) and continues that conversation when the
|
|
71
|
+
key recurs.
|
|
72
|
+
|
|
73
|
+
## Examples
|
|
74
|
+
|
|
75
|
+
See [`examples/`](examples/):
|
|
76
|
+
|
|
77
|
+
- [`mistral_durable_agent.py`](examples/mistral_durable_agent.py) — a single durable
|
|
78
|
+
agent: tools as Flyte tasks, per-turn traced conversation, agent timeline in the
|
|
79
|
+
report.
|
|
80
|
+
- [`mistral_crash_resume.py`](examples/mistral_crash_resume.py) — crash & resume:
|
|
81
|
+
the task crashes on its first attempt after doing real work; on retry the completed
|
|
82
|
+
conversation turns replay from their `flyte.trace` records and the tool calls are
|
|
83
|
+
cache hits. Run on a backend to see the replay.
|
|
84
|
+
- [`mistral_multi_agent.py`](examples/mistral_multi_agent.py) — multi-agent
|
|
85
|
+
orchestration: a planner agent decomposes a topic, researcher agents fan out in
|
|
86
|
+
parallel, an editor agent synthesizes — each agent its own durable action.
|
|
87
|
+
- [`mistral_agent_id.py`](examples/mistral_agent_id.py) — drive a pre-created
|
|
88
|
+
server-side agent by `agent_id` (instead of an inline model) while its tool calls
|
|
89
|
+
still run as durable Flyte actions.
|
|
90
|
+
- [`mistral_memory.py`](examples/mistral_memory.py) — cross-run memory: two
|
|
91
|
+
separate runs share a `memory_key`; the agent learns a fact in run 1 and recalls
|
|
92
|
+
it in run 2.
|
|
93
|
+
- [`mistral_handoffs.py`](examples/mistral_handoffs.py) — native handoffs: a triage
|
|
94
|
+
agent hands the conversation off to a billing or technical-support agent (by id),
|
|
95
|
+
the whole multi-agent run durable on Flyte. The specialist can pause on a Flyte
|
|
96
|
+
condition (`flyte.new_condition`) to have a human share a detail, then resume with it.
|
|
97
|
+
|
|
98
|
+
## Conformance
|
|
99
|
+
|
|
100
|
+
This adapter passes the shared `flyteplugins.agents.core.testing.assert_adapter_conforms`
|
|
101
|
+
check — the same one every adapter runs — so it follows the common format despite
|
|
102
|
+
a server-side, conversation-based SDK shape very different from OpenAI's or
|
|
103
|
+
Claude's.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
flyteplugins/agents/mistral/__init__.py,sha256=kcPvFbQ6At4gdUwjsipQQM_yIlumjuZHe6wpj6tlEHg,620
|
|
2
|
+
flyteplugins/agents/mistral/_run.py,sha256=G3JRI79TUCKlmzDO8kX7a31sGE5pMbK9XNr4EM2vCiA,11271
|
|
3
|
+
flyteplugins_agents_mistral-2.5.9.dist-info/METADATA,sha256=-ST0wUViMN4dVE-CHQMl-NwCQaWtDaf5TH1KNTM7nRM,4433
|
|
4
|
+
flyteplugins_agents_mistral-2.5.9.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
5
|
+
flyteplugins_agents_mistral-2.5.9.dist-info/top_level.txt,sha256=cgd779rPu9EsvdtuYgUxNHHgElaQvPn74KhB5XSeMBE,13
|
|
6
|
+
flyteplugins_agents_mistral-2.5.9.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
flyteplugins
|