deploypod 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.
- deploypod/__init__.py +31 -0
- deploypod/_http.py +73 -0
- deploypod/auth.py +90 -0
- deploypod/client.py +122 -0
- deploypod/env.py +49 -0
- deploypod/exceptions.py +39 -0
- deploypod/py.typed +0 -0
- deploypod/resources/__init__.py +0 -0
- deploypod/resources/agents.py +430 -0
- deploypod/resources/async_tasks.py +84 -0
- deploypod/resources/connections.py +57 -0
- deploypod/resources/conversations.py +41 -0
- deploypod/resources/custom_tools.py +126 -0
- deploypod/resources/groups.py +71 -0
- deploypod/resources/jobs.py +28 -0
- deploypod/resources/knowledge_bases.py +47 -0
- deploypod/resources/knowledge_sources.py +141 -0
- deploypod/resources/mcp_servers.py +117 -0
- deploypod/resources/organization.py +150 -0
- deploypod/resources/providers.py +64 -0
- deploypod/resources/skills.py +75 -0
- deploypod/resources/users.py +56 -0
- deploypod/types.py +390 -0
- deploypod-0.1.0.dist-info/METADATA +104 -0
- deploypod-0.1.0.dist-info/RECORD +28 -0
- deploypod-0.1.0.dist-info/WHEEL +5 -0
- deploypod-0.1.0.dist-info/licenses/LICENSE +21 -0
- deploypod-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import mimetypes
|
|
4
|
+
import re
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
import httpx
|
|
10
|
+
|
|
11
|
+
from deploypod._http import json_body, message_result, raise_for_status
|
|
12
|
+
from deploypod.types import (
|
|
13
|
+
AgentResponse,
|
|
14
|
+
DeploymentResponse,
|
|
15
|
+
MessageResult,
|
|
16
|
+
TestPruningResponse,
|
|
17
|
+
ThirdPartyAuthResponse,
|
|
18
|
+
WalkthroughQuestionResponse,
|
|
19
|
+
WelcomeMessageResponse,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AgentsResource:
|
|
24
|
+
def __init__(self, http: httpx.Client):
|
|
25
|
+
self._http = http
|
|
26
|
+
|
|
27
|
+
# ── CRUD ─────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
def list(self) -> list[AgentResponse]:
|
|
30
|
+
resp = self._http.get("/agents/")
|
|
31
|
+
raise_for_status(resp)
|
|
32
|
+
return [AgentResponse(**a) for a in resp.json()]
|
|
33
|
+
|
|
34
|
+
def create(
|
|
35
|
+
self,
|
|
36
|
+
*,
|
|
37
|
+
name: str,
|
|
38
|
+
knowledge_base_ids: list[int],
|
|
39
|
+
llm_provider_id: int | None = None,
|
|
40
|
+
llm_model: str | None = None,
|
|
41
|
+
group_id: int | None = None,
|
|
42
|
+
system_prompt_extension: str | None = None,
|
|
43
|
+
autonomy_mode: str | None = None,
|
|
44
|
+
can_use_web_search: bool | None = None,
|
|
45
|
+
table_manager_enabled: bool | None = None,
|
|
46
|
+
writable_kb_ids: list[int] | None = None,
|
|
47
|
+
max_iterations: int | None = None,
|
|
48
|
+
tts_provider: str | None = None,
|
|
49
|
+
tts_model: str | None = None,
|
|
50
|
+
tts_voice: str | None = None,
|
|
51
|
+
speech_speed: float | None = None,
|
|
52
|
+
welcome_message_ids: list[str] | None = None,
|
|
53
|
+
output_schema: dict[str, Any] | None = None,
|
|
54
|
+
) -> AgentResponse:
|
|
55
|
+
payload: dict[str, Any] = {
|
|
56
|
+
"name": name,
|
|
57
|
+
"knowledge_base_ids": knowledge_base_ids,
|
|
58
|
+
}
|
|
59
|
+
_set_optional(payload, "llm_provider_id", llm_provider_id)
|
|
60
|
+
_set_optional(payload, "llm_model", llm_model)
|
|
61
|
+
_set_optional(payload, "group_id", group_id)
|
|
62
|
+
_set_optional(payload, "system_prompt_extension", system_prompt_extension)
|
|
63
|
+
_set_optional(payload, "autonomy_mode", autonomy_mode)
|
|
64
|
+
_set_optional(payload, "can_use_web_search", can_use_web_search)
|
|
65
|
+
_set_optional(payload, "table_manager_enabled", table_manager_enabled)
|
|
66
|
+
_set_optional(payload, "writable_kb_ids", writable_kb_ids)
|
|
67
|
+
_set_optional(payload, "max_iterations", max_iterations)
|
|
68
|
+
_set_optional(payload, "tts_provider", tts_provider)
|
|
69
|
+
_set_optional(payload, "tts_model", tts_model)
|
|
70
|
+
_set_optional(payload, "tts_voice", tts_voice)
|
|
71
|
+
_set_optional(payload, "speech_speed", speech_speed)
|
|
72
|
+
_set_optional(payload, "welcome_message_ids", welcome_message_ids)
|
|
73
|
+
_set_optional(payload, "output_schema", output_schema)
|
|
74
|
+
|
|
75
|
+
resp = self._http.post("/agent/create", json=payload)
|
|
76
|
+
raise_for_status(resp)
|
|
77
|
+
|
|
78
|
+
# Go API returns JSON with uuid; legacy Python returned HTML embed script.
|
|
79
|
+
content_type = resp.headers.get("content-type", "")
|
|
80
|
+
if "application/json" in content_type:
|
|
81
|
+
data = resp.json()
|
|
82
|
+
return self.get(data["uuid"])
|
|
83
|
+
|
|
84
|
+
# Fallback: extract UUID from HTML embed script
|
|
85
|
+
match = re.search(r"/widget/([0-9a-f-]{36})/embed\.js", resp.text)
|
|
86
|
+
if not match:
|
|
87
|
+
raise ValueError(f"Could not parse agent UUID from create response: {resp.text[:200]}")
|
|
88
|
+
return self.get(match.group(1))
|
|
89
|
+
|
|
90
|
+
def get(self, agent_uuid: str) -> AgentResponse:
|
|
91
|
+
resp = self._http.get(f"/agent/{agent_uuid}")
|
|
92
|
+
raise_for_status(resp)
|
|
93
|
+
return AgentResponse(**resp.json())
|
|
94
|
+
|
|
95
|
+
def update(self, agent_id: int, **kwargs: Any) -> AgentResponse:
|
|
96
|
+
resp = self._http.put(f"/agent/{agent_id}", json=kwargs)
|
|
97
|
+
raise_for_status(resp)
|
|
98
|
+
return AgentResponse(**resp.json())
|
|
99
|
+
|
|
100
|
+
def delete(self, agent_id: int) -> MessageResult:
|
|
101
|
+
resp = self._http.delete(f"/agent/{agent_id}")
|
|
102
|
+
raise_for_status(resp)
|
|
103
|
+
# The endpoint answers 204 with no body.
|
|
104
|
+
if not resp.content:
|
|
105
|
+
return MessageResult(message="Agent deleted")
|
|
106
|
+
return message_result(resp)
|
|
107
|
+
|
|
108
|
+
def list_deleted(self) -> list[AgentResponse]:
|
|
109
|
+
resp = self._http.get("/agent/deleted")
|
|
110
|
+
raise_for_status(resp)
|
|
111
|
+
return [AgentResponse(**a) for a in resp.json()]
|
|
112
|
+
|
|
113
|
+
def restore(self, agent_id: int) -> AgentResponse:
|
|
114
|
+
resp = self._http.post(f"/agent/{agent_id}/restore")
|
|
115
|
+
raise_for_status(resp)
|
|
116
|
+
return AgentResponse(**resp.json())
|
|
117
|
+
|
|
118
|
+
# ── Run ──────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
def run(self, agent_uuid: str, prompt: str, *, timeout: Optional[float] = None, **options: Any) -> dict:
|
|
121
|
+
"""Run the agent once and wait for the result (POST /agent/{uuid}/run).
|
|
122
|
+
|
|
123
|
+
``options`` are the run request's other fields, e.g. ``run_skills=True``,
|
|
124
|
+
``run_exploration=True``, ``output_schema={...}``, ``max_iterations=8``.
|
|
125
|
+
Returns the run result: ``status``, ``final_emit``, ``result_summary``,
|
|
126
|
+
``iterations``, ``usage``, ``execution_time_ms``, ``error_message``.
|
|
127
|
+
A run can take longer than the client's default timeout; pass ``timeout``
|
|
128
|
+
(seconds) for this call.
|
|
129
|
+
"""
|
|
130
|
+
extra: Dict[str, Any] = {} if timeout is None else {"timeout": timeout}
|
|
131
|
+
resp = self._http.post(f"/agent/{agent_uuid}/run", json={"prompt": prompt, **options}, **extra)
|
|
132
|
+
raise_for_status(resp)
|
|
133
|
+
return resp.json()
|
|
134
|
+
|
|
135
|
+
# ── Deployment ───────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
def deploy(self, agent_id: int, **kwargs: Any) -> dict:
|
|
138
|
+
resp = self._http.post(f"/agent/{agent_id}/deploy", json=kwargs)
|
|
139
|
+
raise_for_status(resp)
|
|
140
|
+
return resp.json()
|
|
141
|
+
|
|
142
|
+
def get_deployment(self, agent_id: int) -> dict:
|
|
143
|
+
resp = self._http.get(f"/agent/{agent_id}/deploy")
|
|
144
|
+
raise_for_status(resp)
|
|
145
|
+
return resp.json()
|
|
146
|
+
|
|
147
|
+
def update_deployment(self, agent_id: int, **kwargs: Any) -> dict:
|
|
148
|
+
resp = self._http.put(f"/agent/{agent_id}/deploy", json=kwargs)
|
|
149
|
+
raise_for_status(resp)
|
|
150
|
+
return resp.json()
|
|
151
|
+
|
|
152
|
+
def undeploy(self, agent_id: int) -> MessageResult:
|
|
153
|
+
resp = self._http.delete(f"/agent/{agent_id}/deploy")
|
|
154
|
+
raise_for_status(resp)
|
|
155
|
+
return message_result(resp)
|
|
156
|
+
|
|
157
|
+
# ── Third-party auth ─────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
def enable_third_party_auth(self, agent_uuid: str) -> ThirdPartyAuthResponse:
|
|
160
|
+
resp = self._http.post(f"/agent/{agent_uuid}/third-party-auth/enable")
|
|
161
|
+
raise_for_status(resp)
|
|
162
|
+
return ThirdPartyAuthResponse(**resp.json())
|
|
163
|
+
|
|
164
|
+
def disable_third_party_auth(self, agent_uuid: str) -> MessageResult:
|
|
165
|
+
resp = self._http.post(f"/agent/{agent_uuid}/third-party-auth/disable")
|
|
166
|
+
raise_for_status(resp)
|
|
167
|
+
return message_result(resp)
|
|
168
|
+
|
|
169
|
+
def get_shared_secret(self, agent_uuid: str) -> ThirdPartyAuthResponse:
|
|
170
|
+
resp = self._http.get(f"/agent/{agent_uuid}/third-party-auth")
|
|
171
|
+
raise_for_status(resp)
|
|
172
|
+
return ThirdPartyAuthResponse(**resp.json())
|
|
173
|
+
|
|
174
|
+
def regenerate_secret(self, agent_uuid: str) -> ThirdPartyAuthResponse:
|
|
175
|
+
resp = self._http.post(f"/agent/{agent_uuid}/third-party-auth/regenerate")
|
|
176
|
+
raise_for_status(resp)
|
|
177
|
+
return ThirdPartyAuthResponse(**resp.json())
|
|
178
|
+
|
|
179
|
+
def validate_token(self, agent_uuid: str, token: str) -> dict:
|
|
180
|
+
resp = self._http.post(f"/agent/{agent_uuid}/validate-token", json={"token": token})
|
|
181
|
+
raise_for_status(resp)
|
|
182
|
+
return resp.json()
|
|
183
|
+
|
|
184
|
+
# ── Welcome messages ─────────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
def list_welcome_messages(self, agent_id: int) -> list[WelcomeMessageResponse]:
|
|
187
|
+
resp = self._http.get(f"/agent/{agent_id}/welcome-messages")
|
|
188
|
+
raise_for_status(resp)
|
|
189
|
+
return [WelcomeMessageResponse(**m) for m in resp.json()]
|
|
190
|
+
|
|
191
|
+
def update_welcome_messages(self, agent_id: int, welcome_message_ids: list[str]) -> dict:
|
|
192
|
+
resp = self._http.put(
|
|
193
|
+
f"/agent/{agent_id}/welcome-messages",
|
|
194
|
+
json={"welcome_message_ids": welcome_message_ids},
|
|
195
|
+
)
|
|
196
|
+
raise_for_status(resp)
|
|
197
|
+
return resp.json()
|
|
198
|
+
|
|
199
|
+
def create_welcome_message(
|
|
200
|
+
self, *, name: str, message: str, is_template: bool = False,
|
|
201
|
+
template_variables: list[str] | None = None,
|
|
202
|
+
) -> dict:
|
|
203
|
+
payload: dict[str, Any] = {"name": name, "message": message, "is_template": is_template}
|
|
204
|
+
_set_optional(payload, "template_variables", template_variables)
|
|
205
|
+
resp = self._http.post("/agent/welcome-messages", json=payload)
|
|
206
|
+
raise_for_status(resp)
|
|
207
|
+
return resp.json()
|
|
208
|
+
|
|
209
|
+
def get_welcome_message(self, message_id: str) -> dict:
|
|
210
|
+
resp = self._http.get(f"/agent/welcome-messages/{message_id}")
|
|
211
|
+
raise_for_status(resp)
|
|
212
|
+
return resp.json()
|
|
213
|
+
|
|
214
|
+
def update_welcome_message(self, message_id: str, **kwargs: Any) -> dict:
|
|
215
|
+
resp = self._http.put(f"/agent/welcome-messages/{message_id}", json=kwargs)
|
|
216
|
+
raise_for_status(resp)
|
|
217
|
+
return resp.json()
|
|
218
|
+
|
|
219
|
+
def delete_welcome_message(self, message_id: str) -> MessageResult:
|
|
220
|
+
resp = self._http.delete(f"/agent/welcome-messages/{message_id}")
|
|
221
|
+
raise_for_status(resp)
|
|
222
|
+
return message_result(resp)
|
|
223
|
+
|
|
224
|
+
# ── Walkthrough questions ────────────────────────────────────
|
|
225
|
+
|
|
226
|
+
def list_walkthrough_questions(self, agent_id: int) -> list[WalkthroughQuestionResponse]:
|
|
227
|
+
resp = self._http.get(f"/agent/{agent_id}/walkthrough-questions")
|
|
228
|
+
raise_for_status(resp)
|
|
229
|
+
return [WalkthroughQuestionResponse(**q) for q in resp.json()]
|
|
230
|
+
|
|
231
|
+
def create_walkthrough_question(
|
|
232
|
+
self, agent_id: int, *, question_text: str, description: str | None = None,
|
|
233
|
+
category: str | None = None,
|
|
234
|
+
) -> dict:
|
|
235
|
+
payload: dict[str, Any] = {"question_text": question_text}
|
|
236
|
+
_set_optional(payload, "description", description)
|
|
237
|
+
_set_optional(payload, "category", category)
|
|
238
|
+
resp = self._http.post(f"/agent/{agent_id}/walkthrough-questions", json=payload)
|
|
239
|
+
raise_for_status(resp)
|
|
240
|
+
return resp.json()
|
|
241
|
+
|
|
242
|
+
def update_walkthrough_question(self, agent_id: int, question_id: str, **kwargs: Any) -> dict:
|
|
243
|
+
resp = self._http.put(f"/agent/{agent_id}/walkthrough-questions/{question_id}", json=kwargs)
|
|
244
|
+
raise_for_status(resp)
|
|
245
|
+
return resp.json()
|
|
246
|
+
|
|
247
|
+
def delete_walkthrough_question(self, agent_id: int, question_id: str) -> MessageResult:
|
|
248
|
+
resp = self._http.delete(f"/agent/{agent_id}/walkthrough-questions/{question_id}")
|
|
249
|
+
raise_for_status(resp)
|
|
250
|
+
return message_result(resp)
|
|
251
|
+
|
|
252
|
+
def reorder_walkthrough_questions(self, agent_id: int, question_ids: list[str]) -> dict:
|
|
253
|
+
resp = self._http.put(
|
|
254
|
+
f"/agent/{agent_id}/walkthrough-questions-reorder",
|
|
255
|
+
json={"question_ids": question_ids},
|
|
256
|
+
)
|
|
257
|
+
raise_for_status(resp)
|
|
258
|
+
return resp.json()
|
|
259
|
+
|
|
260
|
+
def generate_walkthrough_questions(self, agent_id: int) -> list[dict]:
|
|
261
|
+
resp = self._http.post(f"/agent/{agent_id}/walkthrough-questions/generate")
|
|
262
|
+
raise_for_status(resp)
|
|
263
|
+
return resp.json()
|
|
264
|
+
|
|
265
|
+
def add_walkthrough_attachment(
|
|
266
|
+
self, agent_id: int, question_id: str, file_path: str | Path,
|
|
267
|
+
) -> dict:
|
|
268
|
+
"""Attach a file to a walkthrough question.
|
|
269
|
+
|
|
270
|
+
The upload runs the same extraction the chat upload does, so a large
|
|
271
|
+
PDF can take longer than the client's default timeout; construct the
|
|
272
|
+
client with a larger ``timeout`` when seeding big files.
|
|
273
|
+
"""
|
|
274
|
+
file_path = Path(file_path)
|
|
275
|
+
content_type = mimetypes.guess_type(file_path.name)[0] or "application/octet-stream"
|
|
276
|
+
with open(file_path, "rb") as fp:
|
|
277
|
+
resp = self._http.post(
|
|
278
|
+
f"/agent/{agent_id}/walkthrough-questions/{question_id}/attachments",
|
|
279
|
+
files={"file": (file_path.name, fp, content_type)},
|
|
280
|
+
)
|
|
281
|
+
raise_for_status(resp)
|
|
282
|
+
return resp.json()
|
|
283
|
+
|
|
284
|
+
def remove_walkthrough_attachment(
|
|
285
|
+
self, agent_id: int, question_id: str, index: int,
|
|
286
|
+
) -> dict:
|
|
287
|
+
"""Remove one attachment by position; returns the entry that was removed."""
|
|
288
|
+
resp = self._http.delete(
|
|
289
|
+
f"/agent/{agent_id}/walkthrough-questions/{question_id}/attachments/{index}"
|
|
290
|
+
)
|
|
291
|
+
raise_for_status(resp)
|
|
292
|
+
return json_body(resp)
|
|
293
|
+
|
|
294
|
+
# ── Subagents ────────────────────────────────────────────────
|
|
295
|
+
|
|
296
|
+
def list_subagents(self, agent_id: int) -> list[dict]:
|
|
297
|
+
resp = self._http.get(f"/agent/{agent_id}/subagents/")
|
|
298
|
+
raise_for_status(resp)
|
|
299
|
+
return resp.json()
|
|
300
|
+
|
|
301
|
+
def assign_subagent(self, agent_id: int, subagent_id: int, **kwargs: Any) -> dict:
|
|
302
|
+
payload = {"subagent_id": subagent_id, **kwargs}
|
|
303
|
+
resp = self._http.post(f"/agent/{agent_id}/subagents/", json=payload)
|
|
304
|
+
raise_for_status(resp)
|
|
305
|
+
return resp.json()
|
|
306
|
+
|
|
307
|
+
def update_subagent(self, agent_id: int, subagent_id: int, **kwargs: Any) -> dict:
|
|
308
|
+
resp = self._http.put(f"/agent/{agent_id}/subagents/{subagent_id}", json=kwargs)
|
|
309
|
+
raise_for_status(resp)
|
|
310
|
+
return resp.json()
|
|
311
|
+
|
|
312
|
+
def remove_subagent(self, agent_id: int, subagent_id: int) -> MessageResult:
|
|
313
|
+
resp = self._http.delete(f"/agent/{agent_id}/subagents/{subagent_id}")
|
|
314
|
+
raise_for_status(resp)
|
|
315
|
+
# The endpoint answers {"status": "ok"}, not {"message": ...}.
|
|
316
|
+
body = resp.json() if resp.content else {}
|
|
317
|
+
return MessageResult(message=body.get("message") or body.get("status") or "Subagent removed")
|
|
318
|
+
|
|
319
|
+
# ── Long-term memory ─────────────────────────────────────────
|
|
320
|
+
|
|
321
|
+
def list_memories(self, agent_id: int) -> list[dict]:
|
|
322
|
+
resp = self._http.get(f"/agent/{agent_id}/memories")
|
|
323
|
+
raise_for_status(resp)
|
|
324
|
+
return resp.json()
|
|
325
|
+
|
|
326
|
+
def create_memory(self, agent_id: int, *, content: str, **kwargs: Any) -> dict:
|
|
327
|
+
payload = {"content": content, **kwargs}
|
|
328
|
+
resp = self._http.post(f"/agent/{agent_id}/memories", json=payload)
|
|
329
|
+
raise_for_status(resp)
|
|
330
|
+
return resp.json()
|
|
331
|
+
|
|
332
|
+
def search_memories(self, agent_id: int, *, query: str, top_k: int = 5) -> list[dict]:
|
|
333
|
+
resp = self._http.post(
|
|
334
|
+
f"/agent/{agent_id}/memories/search",
|
|
335
|
+
json={"query": query, "top_k": top_k},
|
|
336
|
+
)
|
|
337
|
+
raise_for_status(resp)
|
|
338
|
+
return resp.json()
|
|
339
|
+
|
|
340
|
+
def delete_memory(self, agent_id: int, memory_id: int) -> MessageResult:
|
|
341
|
+
resp = self._http.delete(f"/agent/{agent_id}/memories/{memory_id}")
|
|
342
|
+
raise_for_status(resp)
|
|
343
|
+
return message_result(resp)
|
|
344
|
+
|
|
345
|
+
def delete_all_memories(self, agent_id: int) -> MessageResult:
|
|
346
|
+
resp = self._http.delete(f"/agent/{agent_id}/memories/")
|
|
347
|
+
raise_for_status(resp)
|
|
348
|
+
return message_result(resp)
|
|
349
|
+
|
|
350
|
+
# ── Managed tables ───────────────────────────────────────────
|
|
351
|
+
|
|
352
|
+
def list_managed_tables(self, agent_id: int) -> list[dict]:
|
|
353
|
+
resp = self._http.get(f"/agent/{agent_id}/managed-tables")
|
|
354
|
+
raise_for_status(resp)
|
|
355
|
+
return resp.json()
|
|
356
|
+
|
|
357
|
+
def get_managed_table(self, agent_id: int, table_name: str) -> dict:
|
|
358
|
+
resp = self._http.get(f"/agent/{agent_id}/managed-tables/{table_name}")
|
|
359
|
+
raise_for_status(resp)
|
|
360
|
+
return resp.json()
|
|
361
|
+
|
|
362
|
+
def query_managed_table(self, agent_id: int, *, sql: str, limit: int = 100) -> dict:
|
|
363
|
+
resp = self._http.post(
|
|
364
|
+
f"/agent/{agent_id}/managed-tables/query",
|
|
365
|
+
json={"sql": sql, "limit": limit},
|
|
366
|
+
)
|
|
367
|
+
raise_for_status(resp)
|
|
368
|
+
return resp.json()
|
|
369
|
+
|
|
370
|
+
def delete_managed_table(self, agent_id: int, table_name: str) -> MessageResult:
|
|
371
|
+
resp = self._http.delete(f"/agent/{agent_id}/managed-tables/{table_name}")
|
|
372
|
+
raise_for_status(resp)
|
|
373
|
+
return message_result(resp)
|
|
374
|
+
|
|
375
|
+
# ── Pruning test ──────────────────────────────────────────────
|
|
376
|
+
|
|
377
|
+
def test_pruning(self, agent_id: int, query: str) -> TestPruningResponse:
|
|
378
|
+
resp = self._http.post("/chat/test-pruning", json={"agent_id": agent_id, "query": query})
|
|
379
|
+
raise_for_status(resp)
|
|
380
|
+
return TestPruningResponse(**resp.json())
|
|
381
|
+
|
|
382
|
+
# ── Embed script ─────────────────────────────────────────────
|
|
383
|
+
|
|
384
|
+
def get_embed_script(self, agent_uuid: str) -> str:
|
|
385
|
+
resp = self._http.get(f"/agent/{agent_uuid}/script")
|
|
386
|
+
raise_for_status(resp)
|
|
387
|
+
return resp.text
|
|
388
|
+
|
|
389
|
+
# ── Single-shot LLM completion ───────────────────────────────
|
|
390
|
+
|
|
391
|
+
def completion(
|
|
392
|
+
self,
|
|
393
|
+
agent_uuid: str,
|
|
394
|
+
*,
|
|
395
|
+
user_prompt: str,
|
|
396
|
+
system_prompt: str | None = None,
|
|
397
|
+
tools: list[dict[str, Any]] | None = None,
|
|
398
|
+
tool_choice: str | dict[str, Any] | None = None,
|
|
399
|
+
temperature: float | None = None,
|
|
400
|
+
max_tokens: int | None = None,
|
|
401
|
+
metadata: dict[str, str] | None = None,
|
|
402
|
+
) -> dict[str, Any]:
|
|
403
|
+
"""Make a single LLM completion using the agent's configured LLM.
|
|
404
|
+
|
|
405
|
+
Uses the agent's provider, model, api_key and base_url — callers do not
|
|
406
|
+
need their own LLM credentials. Every call is recorded in LLM Monitor
|
|
407
|
+
(set ``metadata={"generation_name": "..."}`` to tag traces).
|
|
408
|
+
|
|
409
|
+
This is NOT an agent pipeline run: no tools execution, no history, no
|
|
410
|
+
reflection. Just one chat-completion call.
|
|
411
|
+
|
|
412
|
+
Returns a dict with keys: ``content``, ``tool_calls``, ``finish_reason``,
|
|
413
|
+
``usage``, ``model``, ``duration_ms``.
|
|
414
|
+
"""
|
|
415
|
+
payload: dict[str, Any] = {"user_prompt": user_prompt}
|
|
416
|
+
_set_optional(payload, "system_prompt", system_prompt)
|
|
417
|
+
_set_optional(payload, "tools", tools)
|
|
418
|
+
_set_optional(payload, "tool_choice", tool_choice)
|
|
419
|
+
_set_optional(payload, "temperature", temperature)
|
|
420
|
+
_set_optional(payload, "max_tokens", max_tokens)
|
|
421
|
+
_set_optional(payload, "metadata", metadata)
|
|
422
|
+
|
|
423
|
+
resp = self._http.post(f"/agent/{agent_uuid}/llm/completion", json=payload)
|
|
424
|
+
raise_for_status(resp)
|
|
425
|
+
return resp.json()
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def _set_optional(d: dict, key: str, value: Any) -> None:
|
|
429
|
+
if value is not None:
|
|
430
|
+
d[key] = value
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from deploypod._http import message_result, raise_for_status
|
|
8
|
+
from deploypod.types import AsyncRunResponse, AsyncTaskResponse, MessageResult
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AsyncTasksResource:
|
|
12
|
+
def __init__(self, http: httpx.Client):
|
|
13
|
+
self._http = http
|
|
14
|
+
|
|
15
|
+
def create(
|
|
16
|
+
self,
|
|
17
|
+
*,
|
|
18
|
+
agent_id: int,
|
|
19
|
+
name: str,
|
|
20
|
+
prompt: str,
|
|
21
|
+
schedule_cron: str,
|
|
22
|
+
timeout_seconds: int | None = None,
|
|
23
|
+
enabled: bool = True,
|
|
24
|
+
) -> AsyncTaskResponse:
|
|
25
|
+
payload: dict[str, Any] = {
|
|
26
|
+
"agent_id": agent_id,
|
|
27
|
+
"name": name,
|
|
28
|
+
"prompt": prompt,
|
|
29
|
+
"schedule_cron": schedule_cron,
|
|
30
|
+
"enabled": enabled,
|
|
31
|
+
}
|
|
32
|
+
if timeout_seconds is not None:
|
|
33
|
+
payload["timeout_seconds"] = timeout_seconds
|
|
34
|
+
resp = self._http.post("/async-agents/tasks/", json=payload)
|
|
35
|
+
raise_for_status(resp)
|
|
36
|
+
return AsyncTaskResponse(**resp.json())
|
|
37
|
+
|
|
38
|
+
def list(self) -> list[AsyncTaskResponse]:
|
|
39
|
+
resp = self._http.get("/async-agents/tasks/")
|
|
40
|
+
raise_for_status(resp)
|
|
41
|
+
return [AsyncTaskResponse(**t) for t in resp.json()]
|
|
42
|
+
|
|
43
|
+
def get(self, task_id: int) -> AsyncTaskResponse:
|
|
44
|
+
resp = self._http.get(f"/async-agents/tasks/{task_id}")
|
|
45
|
+
raise_for_status(resp)
|
|
46
|
+
return AsyncTaskResponse(**resp.json())
|
|
47
|
+
|
|
48
|
+
def update(self, task_id: int, **kwargs: Any) -> MessageResult:
|
|
49
|
+
resp = self._http.put(f"/async-agents/tasks/{task_id}", json=kwargs)
|
|
50
|
+
raise_for_status(resp)
|
|
51
|
+
return message_result(resp)
|
|
52
|
+
|
|
53
|
+
def delete(self, task_id: int) -> MessageResult:
|
|
54
|
+
resp = self._http.delete(f"/async-agents/tasks/{task_id}")
|
|
55
|
+
raise_for_status(resp)
|
|
56
|
+
return message_result(resp)
|
|
57
|
+
|
|
58
|
+
def trigger(self, task_id: int) -> AsyncRunResponse:
|
|
59
|
+
resp = self._http.post(f"/async-agents/tasks/{task_id}/trigger")
|
|
60
|
+
raise_for_status(resp)
|
|
61
|
+
return AsyncRunResponse(**resp.json())
|
|
62
|
+
|
|
63
|
+
def list_runs(self, task_id: int | None = None, agent_id: int | None = None,
|
|
64
|
+
status: str | None = None, limit: int = 50) -> list[AsyncRunResponse]:
|
|
65
|
+
params: dict[str, Any] = {"limit": limit}
|
|
66
|
+
if task_id is not None:
|
|
67
|
+
params["task_id"] = task_id
|
|
68
|
+
if agent_id is not None:
|
|
69
|
+
params["agent_id"] = agent_id
|
|
70
|
+
if status is not None:
|
|
71
|
+
params["status"] = status
|
|
72
|
+
resp = self._http.get("/async-agents/runs/", params=params)
|
|
73
|
+
raise_for_status(resp)
|
|
74
|
+
return [AsyncRunResponse(**r) for r in resp.json()]
|
|
75
|
+
|
|
76
|
+
def get_run(self, run_id: int) -> AsyncRunResponse:
|
|
77
|
+
resp = self._http.get(f"/async-agents/runs/{run_id}")
|
|
78
|
+
raise_for_status(resp)
|
|
79
|
+
return AsyncRunResponse(**resp.json())
|
|
80
|
+
|
|
81
|
+
def delete_run(self, run_id: int) -> MessageResult:
|
|
82
|
+
resp = self._http.delete(f"/async-agents/runs/{run_id}")
|
|
83
|
+
raise_for_status(resp)
|
|
84
|
+
return message_result(resp)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from deploypod._http import message_result, raise_for_status
|
|
8
|
+
from deploypod.types import ConnectionResponse, MessageResult, TableResponse
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ConnectionsResource:
|
|
12
|
+
def __init__(self, http: httpx.Client):
|
|
13
|
+
self._http = http
|
|
14
|
+
|
|
15
|
+
def create(self, *, name: str, db_type: str, **kwargs: Any) -> ConnectionResponse:
|
|
16
|
+
payload = {"name": name, "db_type": db_type, **kwargs}
|
|
17
|
+
resp = self._http.post("/connections/", json=payload)
|
|
18
|
+
raise_for_status(resp)
|
|
19
|
+
return ConnectionResponse(**resp.json())
|
|
20
|
+
|
|
21
|
+
def list(self) -> list[ConnectionResponse]:
|
|
22
|
+
resp = self._http.get("/connections/")
|
|
23
|
+
raise_for_status(resp)
|
|
24
|
+
return [ConnectionResponse(**c) for c in resp.json()]
|
|
25
|
+
|
|
26
|
+
def get(self, conn_id: int) -> ConnectionResponse:
|
|
27
|
+
resp = self._http.get(f"/connections/{conn_id}")
|
|
28
|
+
raise_for_status(resp)
|
|
29
|
+
return ConnectionResponse(**resp.json())
|
|
30
|
+
|
|
31
|
+
def update(self, conn_id: int, **kwargs: Any) -> ConnectionResponse:
|
|
32
|
+
resp = self._http.put(f"/connections/{conn_id}", json=kwargs)
|
|
33
|
+
raise_for_status(resp)
|
|
34
|
+
return ConnectionResponse(**resp.json())
|
|
35
|
+
|
|
36
|
+
def delete(self, conn_id: int) -> MessageResult:
|
|
37
|
+
resp = self._http.delete(f"/connections/{conn_id}")
|
|
38
|
+
raise_for_status(resp)
|
|
39
|
+
return message_result(resp)
|
|
40
|
+
|
|
41
|
+
def test(self, conn_id: int) -> dict:
|
|
42
|
+
resp = self._http.post(f"/connections/{conn_id}/test")
|
|
43
|
+
raise_for_status(resp)
|
|
44
|
+
return resp.json()
|
|
45
|
+
|
|
46
|
+
def list_schemas(self, conn_id: int) -> list[dict]:
|
|
47
|
+
resp = self._http.get(f"/connections/{conn_id}/schemas")
|
|
48
|
+
raise_for_status(resp)
|
|
49
|
+
return resp.json()
|
|
50
|
+
|
|
51
|
+
def list_tables(self, conn_id: int, *, schema_name: str | None = None) -> list[dict]:
|
|
52
|
+
if schema_name:
|
|
53
|
+
resp = self._http.get(f"/connections/{conn_id}/schemas/{schema_name}/tables")
|
|
54
|
+
else:
|
|
55
|
+
resp = self._http.get(f"/connections/{conn_id}/tables")
|
|
56
|
+
raise_for_status(resp)
|
|
57
|
+
return resp.json()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from deploypod._http import message_result, raise_for_status
|
|
8
|
+
from deploypod.types import ConversationDetailResponse, ConversationResponse, MessageResult
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ConversationsResource:
|
|
12
|
+
def __init__(self, http: httpx.Client):
|
|
13
|
+
self._http = http
|
|
14
|
+
|
|
15
|
+
def list(self, *, agent_id: int | None = None, skip: int = 0, limit: int = 50) -> list[ConversationResponse]:
|
|
16
|
+
params: dict[str, Any] = {"skip": skip, "limit": limit}
|
|
17
|
+
if agent_id is not None:
|
|
18
|
+
params["agent_id"] = agent_id
|
|
19
|
+
resp = self._http.get("/chat/conversations", params=params)
|
|
20
|
+
raise_for_status(resp)
|
|
21
|
+
return [ConversationResponse(**c) for c in resp.json()]
|
|
22
|
+
|
|
23
|
+
def get(self, conversation_id: str) -> ConversationDetailResponse:
|
|
24
|
+
resp = self._http.get(f"/chat/conversation/{conversation_id}")
|
|
25
|
+
raise_for_status(resp)
|
|
26
|
+
return ConversationDetailResponse(**resp.json())
|
|
27
|
+
|
|
28
|
+
def get_messages(self, conversation_id: str) -> list[dict]:
|
|
29
|
+
resp = self._http.get(f"/chat/conversation/{conversation_id}/messages")
|
|
30
|
+
raise_for_status(resp)
|
|
31
|
+
return resp.json()
|
|
32
|
+
|
|
33
|
+
def delete(self, conversation_id: str) -> MessageResult:
|
|
34
|
+
resp = self._http.delete(f"/chat/conversation/{conversation_id}")
|
|
35
|
+
raise_for_status(resp)
|
|
36
|
+
return message_result(resp)
|
|
37
|
+
|
|
38
|
+
def batch_delete(self, conversation_ids: list[str]) -> MessageResult:
|
|
39
|
+
resp = self._http.request("DELETE", "/chat/conversations/batch", json={"ids": conversation_ids})
|
|
40
|
+
raise_for_status(resp)
|
|
41
|
+
return message_result(resp)
|