daguito-sdk 0.3.0__tar.gz
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.
- daguito_sdk-0.3.0/PKG-INFO +445 -0
- daguito_sdk-0.3.0/README.md +414 -0
- daguito_sdk-0.3.0/pyproject.toml +67 -0
- daguito_sdk-0.3.0/src/daguito/__init__.py +108 -0
- daguito_sdk-0.3.0/src/daguito/_url.py +31 -0
- daguito_sdk-0.3.0/src/daguito/emitter.py +55 -0
- daguito_sdk-0.3.0/src/daguito/knowledge_session.py +203 -0
- daguito_sdk-0.3.0/src/daguito/py.typed +0 -0
- daguito_sdk-0.3.0/src/daguito/types.py +162 -0
- daguito_sdk-0.3.0/src/daguito/webhook_session.py +93 -0
- daguito_sdk-0.3.0/src/daguito/webhook_stream_session.py +502 -0
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: daguito-sdk
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Official Python SDK for Daguito — conversational AI flows, streaming webhooks, and RAG.
|
|
5
|
+
Keywords: daguito,ai,llm,rag,agents,streaming,chatbot
|
|
6
|
+
Author: Daguito
|
|
7
|
+
Author-email: Daguito <daguito.contact@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Dist: httpx>=0.27,<1.0
|
|
20
|
+
Requires-Dist: websockets>=12.0,<14.0
|
|
21
|
+
Requires-Dist: pytest>=8.0 ; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
|
|
23
|
+
Requires-Dist: mypy>=1.10 ; extra == 'dev'
|
|
24
|
+
Requires-Dist: ruff>=0.5 ; extra == 'dev'
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Project-URL: Homepage, https://daguito.com
|
|
27
|
+
Project-URL: Source, https://github.com/daguito/daguito
|
|
28
|
+
Project-URL: Issues, https://github.com/daguito/daguito/issues
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<a href="https://daguito.com" target="_blank">
|
|
34
|
+
<img src="https://raw.githubusercontent.com/daguitocontact-lang/daguito-python/main/assets/logo.png" alt="Daguito" width="160" />
|
|
35
|
+
</a>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
<h1 align="center">daguito (Python SDK)</h1>
|
|
39
|
+
|
|
40
|
+
<p align="center">
|
|
41
|
+
Official Python SDK for the
|
|
42
|
+
<a href="https://daguito.com">Daguito</a>
|
|
43
|
+
conversational AI platform —
|
|
44
|
+
text, voice, image, and multimodal agent flows.
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
<p align="center">
|
|
48
|
+
<a href="https://pypi.org/project/daguito-sdk/"><img src="https://img.shields.io/pypi/v/daguito-sdk.svg?style=flat-square&color=0a0a0a" alt="pypi version" /></a>
|
|
49
|
+
<a href="https://pypi.org/project/daguito-sdk/"><img src="https://img.shields.io/pypi/dm/daguito-sdk.svg?style=flat-square&color=0a0a0a" alt="pypi downloads" /></a>
|
|
50
|
+
<a href="https://github.com/daguitocontact-lang/daguito-python/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/daguito-sdk.svg?style=flat-square&color=0a0a0a" alt="license" /></a>
|
|
51
|
+
<a href="https://pypi.org/project/daguito-sdk/"><img src="https://img.shields.io/pypi/pyversions/daguito-sdk.svg?style=flat-square&color=0a0a0a" alt="python versions" /></a>
|
|
52
|
+
</p>
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
Async-first. Python 3.10+. Built on `httpx` + `websockets`. Type-hinted everywhere. Mirrors the [TypeScript SDK](https://github.com/daguitocontact-lang/js-sdk) feature-for-feature.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv add daguito-sdk
|
|
60
|
+
# or
|
|
61
|
+
pip install daguito-sdk
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
> Package name is `daguito-sdk`. Import name is `daguito` — same pattern as `scikit-learn` / `sklearn`.
|
|
65
|
+
|
|
66
|
+
## What you get
|
|
67
|
+
|
|
68
|
+
- **`run_webhook()`** — one-shot HTTP call to a webhook flow. Wait, get the result.
|
|
69
|
+
- **`WebhookStreamSession`** — long-lived WebSocket for streaming flows. Token streaming, node lifecycle, custom emits. `async with`-friendly, plus `async for event in session.events()`.
|
|
70
|
+
- **`@session.tool(...)`** — register OpenAI-style tools the LLM can invoke. Your handler runs locally, its return value is fed back to the model as the tool result.
|
|
71
|
+
- **`scope={...}` on the session** — server-enforced metadata filter for KB searches. Isolates a conversation to its own ingested files without trusting the LLM with UUIDs.
|
|
72
|
+
- **`KnowledgeSession`** — Knowledge Base ingest + search over the same `sk_dgt_...` API key as the dashboard.
|
|
73
|
+
- **Typed event payloads** — every WS event is a dataclass (`NodeTokenEvent`, `FlowCompletedEvent`, etc.) so editors autocomplete attributes.
|
|
74
|
+
|
|
75
|
+
## Authentication
|
|
76
|
+
|
|
77
|
+
| Surface | Auth | Best for |
|
|
78
|
+
| ------------------------ | --------------------- | --------------------------------------------------- |
|
|
79
|
+
| Webhook (`sk_wh_...`) | Token issued per-flow | Server-to-server, FastAPI/Django backends, scripts |
|
|
80
|
+
| Knowledge (`sk_dgt_...`) | Org-scoped API key | Ingest + search against your own KB |
|
|
81
|
+
|
|
82
|
+
Create webhooks and API keys from your Daguito dashboard.
|
|
83
|
+
|
|
84
|
+
## Quick start
|
|
85
|
+
|
|
86
|
+
### One-shot webhook
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import asyncio
|
|
90
|
+
from daguito import run_webhook, WebhookRunInput
|
|
91
|
+
|
|
92
|
+
async def main():
|
|
93
|
+
result = await run_webhook(WebhookRunInput(
|
|
94
|
+
api_url="https://api.daguito.com",
|
|
95
|
+
token="sk_wh_...",
|
|
96
|
+
input={"question": "What is the price of BTC?"},
|
|
97
|
+
))
|
|
98
|
+
print(result.output)
|
|
99
|
+
|
|
100
|
+
asyncio.run(main())
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Need a synchronous flavor for scripts that aren't in an event loop? Use `run_webhook_sync`:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from daguito import run_webhook_sync, WebhookRunInput
|
|
107
|
+
result = run_webhook_sync(WebhookRunInput(api_url=..., token=..., input={...}))
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Streaming webhook (text agent)
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import asyncio
|
|
114
|
+
from daguito import (
|
|
115
|
+
WebhookStreamSession,
|
|
116
|
+
WebhookStreamOptions,
|
|
117
|
+
text_message,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
async def main():
|
|
121
|
+
opts = WebhookStreamOptions(
|
|
122
|
+
api_url="https://api.daguito.com",
|
|
123
|
+
webhook_id="wh_abc123",
|
|
124
|
+
token="sk_wh_...",
|
|
125
|
+
)
|
|
126
|
+
async with WebhookStreamSession(opts) as session:
|
|
127
|
+
await session.send(text_message("Hola, ¿qué tal?"))
|
|
128
|
+
|
|
129
|
+
async for event_type, payload in session.events():
|
|
130
|
+
if event_type == "node.token":
|
|
131
|
+
print(payload.text, end="", flush=True)
|
|
132
|
+
elif event_type == "node.completed":
|
|
133
|
+
print(f"\n✓ {payload.node_id} ({payload.duration_ms}ms)")
|
|
134
|
+
elif event_type == "flow.completed":
|
|
135
|
+
print(f"\nDone in {payload.elapsed_ms}ms")
|
|
136
|
+
break
|
|
137
|
+
elif event_type == "flow.failed":
|
|
138
|
+
print(f"\nFlow failed: {payload.error}")
|
|
139
|
+
break
|
|
140
|
+
|
|
141
|
+
asyncio.run(main())
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Prefer callbacks? You can also `session.on("node.token", listener)` like the JS SDK — but async iteration is the idiomatic Python pattern and plays nicely with FastAPI's `StreamingResponse`.
|
|
145
|
+
|
|
146
|
+
### Streaming with images
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from daguito import image_url_message, image_multi_message, media_key_message
|
|
150
|
+
|
|
151
|
+
# Hosted on a public URL (works on streaming-webhook surface)
|
|
152
|
+
await session.send(image_url_message(image_url="https://...", text="Describe this"))
|
|
153
|
+
|
|
154
|
+
# Multiple images
|
|
155
|
+
await session.send(image_multi_message(image_urls=[url1, url2], text="Compare"))
|
|
156
|
+
|
|
157
|
+
# Pre-uploaded (you handled the upload elsewhere)
|
|
158
|
+
await session.send(media_key_message(
|
|
159
|
+
kind="image",
|
|
160
|
+
media_key="media/.../abc.jpg",
|
|
161
|
+
mime_type="image/jpeg",
|
|
162
|
+
size_bytes=234_567,
|
|
163
|
+
))
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
> Need to upload a `File` directly from Python? Upload it through your own backend's presigned URL endpoint, then pass the `media_key` to `media_key_message()`. The streaming surface does not mint presigned URLs.
|
|
167
|
+
|
|
168
|
+
### Per-conversation scope (server-enforced KB filter)
|
|
169
|
+
|
|
170
|
+
When you ingest documents that belong to a specific conversation, patient, or workspace, you usually want the chat to only see chunks that match. Pass a `scope` on the session and Daguito **forces** every `search_knowledge_base` call to apply it as a metadata filter — server-side, before Milvus runs the search. The LLM never sees the scope values, so it can't accidentally widen the search, hallucinate a UUID, or leak data across conversations.
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
import uuid
|
|
174
|
+
from daguito import (
|
|
175
|
+
WebhookStreamSession,
|
|
176
|
+
WebhookStreamOptions,
|
|
177
|
+
KnowledgeSession,
|
|
178
|
+
KnowledgeSessionOptions,
|
|
179
|
+
IngestTextInput,
|
|
180
|
+
text_message,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
consultation_uuid = str(uuid.uuid4())
|
|
184
|
+
|
|
185
|
+
# 1. Ingest tagged with the scope key
|
|
186
|
+
async with KnowledgeSession(KnowledgeSessionOptions(...)) as kb:
|
|
187
|
+
await kb.ingest_text(IngestTextInput(
|
|
188
|
+
text=lab_results_text,
|
|
189
|
+
metadata={"consultation_uuid": consultation_uuid, "kind": "lab"},
|
|
190
|
+
))
|
|
191
|
+
|
|
192
|
+
# 2. Open the chat scoped to that consultation
|
|
193
|
+
opts = WebhookStreamOptions(
|
|
194
|
+
api_url="https://api.daguito.com",
|
|
195
|
+
webhook_id="wh_abc123",
|
|
196
|
+
token="sk_wh_...",
|
|
197
|
+
scope={"consultation_uuid": consultation_uuid},
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
async with WebhookStreamSession(opts) as session:
|
|
201
|
+
await session.send(text_message("¿Cómo están las bilirrubinas?"))
|
|
202
|
+
# → search_knowledge_base is forced to filter by consultation_uuid
|
|
203
|
+
# → the LLM can't see chunks from other consultations
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Scope values must be primitives (`str`, `int`, `float`, `bool`). You can stack multiple keys at once (`{"consultation_uuid": "...", "tenant_id": "..."}`) — every search is filtered by all of them.
|
|
207
|
+
|
|
208
|
+
The LLM can still pass its own `metadata_filter` (e.g. to narrow by `document_type`), but **server scope always wins on key conflict** — a hallucinated value can't widen the result set.
|
|
209
|
+
|
|
210
|
+
### Client-side tools (OpenAI-style function calling)
|
|
211
|
+
|
|
212
|
+
Register tools that the LLM can invoke during a session. Your Python handler
|
|
213
|
+
runs locally, and its return value is fed back to the model as the tool
|
|
214
|
+
result — so the LLM continues its reply with the actual data your code
|
|
215
|
+
produced, not a placeholder.
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
import asyncio
|
|
219
|
+
from daguito import (
|
|
220
|
+
WebhookStreamSession,
|
|
221
|
+
WebhookStreamOptions,
|
|
222
|
+
text_message,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
async def main():
|
|
226
|
+
opts = WebhookStreamOptions(
|
|
227
|
+
api_url="https://api.daguito.com",
|
|
228
|
+
webhook_id="wh_abc123",
|
|
229
|
+
token="sk_wh_...",
|
|
230
|
+
)
|
|
231
|
+
async with WebhookStreamSession(opts) as session:
|
|
232
|
+
|
|
233
|
+
@session.tool(
|
|
234
|
+
name="update_soap_section",
|
|
235
|
+
description="Updates a SOAP section with new clinical information.",
|
|
236
|
+
parameters={
|
|
237
|
+
"type": "object",
|
|
238
|
+
"properties": {
|
|
239
|
+
"section": {
|
|
240
|
+
"type": "string",
|
|
241
|
+
"enum": ["subjective", "objective", "assessment", "plan"],
|
|
242
|
+
},
|
|
243
|
+
"subsection": {"type": "string"},
|
|
244
|
+
"content": {"type": "string"},
|
|
245
|
+
"action": {
|
|
246
|
+
"type": "string",
|
|
247
|
+
"enum": ["add", "update", "append"],
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
"required": ["section", "subsection", "content", "action"],
|
|
251
|
+
},
|
|
252
|
+
)
|
|
253
|
+
async def update_soap(args: dict) -> dict:
|
|
254
|
+
# Run your business logic here — write to your DB, call an API,
|
|
255
|
+
# whatever. The returned value goes back to the LLM as the
|
|
256
|
+
# tool result.
|
|
257
|
+
soap_id = await db.update_soap(args)
|
|
258
|
+
return {"success": True, "soap_id": soap_id}
|
|
259
|
+
|
|
260
|
+
await session.send(text_message(
|
|
261
|
+
"Add this to the plan: paracetamol 500mg every 8 hours for 5 days."
|
|
262
|
+
))
|
|
263
|
+
|
|
264
|
+
async for event_type, payload in session.events():
|
|
265
|
+
if event_type == "node.token":
|
|
266
|
+
print(payload.text, end="", flush=True)
|
|
267
|
+
elif event_type == "flow.completed":
|
|
268
|
+
break
|
|
269
|
+
|
|
270
|
+
asyncio.run(main())
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Tools follow the **exact same shape as OpenAI function calling**
|
|
274
|
+
(`name`, `description`, `parameters` as a JSON Schema). They're sent to the
|
|
275
|
+
flow on every turn via `base_input.client_tools`, merged with whatever
|
|
276
|
+
tools the flow already declared statically, and the LLM picks the best
|
|
277
|
+
one for the job.
|
|
278
|
+
|
|
279
|
+
The handler can be sync or async. Throw an exception to surface a failure
|
|
280
|
+
to the LLM with a typed error.
|
|
281
|
+
|
|
282
|
+
### Knowledge Search
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
from daguito import (
|
|
286
|
+
KnowledgeSession,
|
|
287
|
+
KnowledgeSessionOptions,
|
|
288
|
+
IngestTextInput,
|
|
289
|
+
SearchInput,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
opts = KnowledgeSessionOptions(
|
|
293
|
+
api_url="https://api.daguito.com",
|
|
294
|
+
api_key="sk_dgt_...",
|
|
295
|
+
default_source_id="src_abc123",
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
async with KnowledgeSession(opts) as kb:
|
|
299
|
+
# Ingest text — chunks + embeds + indexes server-side
|
|
300
|
+
await kb.ingest_text(IngestTextInput(
|
|
301
|
+
text="MacBook Pro M4 Max with 64GB RAM...",
|
|
302
|
+
metadata={"category": "laptop", "price_usd": 3499},
|
|
303
|
+
))
|
|
304
|
+
|
|
305
|
+
# Search — vector + keyword hybrid
|
|
306
|
+
result = await kb.search(SearchInput(query="laptops para video", top_k=3))
|
|
307
|
+
for hit in result.hits:
|
|
308
|
+
print(hit.score, hit.content, hit.metadata)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
The `api_key` controls scopes. Mint one from the dashboard with `kb:read` and/or `kb:write` actions, optionally limited to specific KBs.
|
|
312
|
+
|
|
313
|
+
## FastAPI streaming example
|
|
314
|
+
|
|
315
|
+
Stream tokens from a Daguito flow straight to the browser via Server-Sent Events:
|
|
316
|
+
|
|
317
|
+
```python
|
|
318
|
+
from fastapi import FastAPI
|
|
319
|
+
from fastapi.responses import StreamingResponse
|
|
320
|
+
from daguito import WebhookStreamSession, WebhookStreamOptions, text_message
|
|
321
|
+
|
|
322
|
+
app = FastAPI()
|
|
323
|
+
|
|
324
|
+
@app.post("/chat")
|
|
325
|
+
async def chat(message: str):
|
|
326
|
+
async def event_stream():
|
|
327
|
+
async with WebhookStreamSession(WebhookStreamOptions(
|
|
328
|
+
api_url="https://api.daguito.com",
|
|
329
|
+
webhook_id="wh_abc123",
|
|
330
|
+
token="sk_wh_...",
|
|
331
|
+
)) as session:
|
|
332
|
+
await session.send(text_message(message))
|
|
333
|
+
|
|
334
|
+
async for event_type, payload in session.events():
|
|
335
|
+
if event_type == "node.token":
|
|
336
|
+
yield f"data: {payload.text}\n\n"
|
|
337
|
+
elif event_type == "node.emit" and payload.kind == "tool_call":
|
|
338
|
+
# The agent invoked a tool the client owns. Forward it so
|
|
339
|
+
# the frontend (or this backend) can execute it locally.
|
|
340
|
+
yield f"event: tool_call\ndata: {payload.data}\n\n"
|
|
341
|
+
elif event_type == "flow.completed":
|
|
342
|
+
yield "event: done\ndata: ok\n\n"
|
|
343
|
+
return
|
|
344
|
+
|
|
345
|
+
return StreamingResponse(event_stream(), media_type="text/event-stream")
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Runtime support
|
|
349
|
+
|
|
350
|
+
| Module | Python 3.10+ | asyncio | Notes |
|
|
351
|
+
| --------- | ------------ | ------- | ------------------------------------------- |
|
|
352
|
+
| `daguito` | ✅ | ✅ | `httpx` + `websockets`. No native deps. |
|
|
353
|
+
|
|
354
|
+
Works on **CPython** and **PyPy**. Plays well with FastAPI, Starlette, aiohttp, Django Channels, anyio-based stacks, and any async runtime. The synchronous `run_webhook_sync()` helper is provided for scripts and Jupyter notebooks that don't have a running event loop.
|
|
355
|
+
|
|
356
|
+
## Event reference
|
|
357
|
+
|
|
358
|
+
### `WebhookStreamSession` events
|
|
359
|
+
|
|
360
|
+
| Event | Payload class | When |
|
|
361
|
+
| ---------------- | --------------------- | ----------------------------- |
|
|
362
|
+
| `ready` | `ReadyEvent` | Socket authenticated |
|
|
363
|
+
| `closed` | `ClosedEvent` | Transport closed |
|
|
364
|
+
| `node.started` | `NodeStartedEvent` | Engine entered a node |
|
|
365
|
+
| `node.token` | `NodeTokenEvent` | LLM streaming token |
|
|
366
|
+
| `node.completed` | `NodeCompletedEvent` | Node finished |
|
|
367
|
+
| `node.failed` | `NodeFailedEvent` | Node errored |
|
|
368
|
+
| `node.emit` | `NodeEmitEvent` | Custom telemetry from a node |
|
|
369
|
+
| `flow.completed` | `FlowCompletedEvent` | Engine finished |
|
|
370
|
+
| `flow.failed` | `FlowFailedEvent` | Engine errored |
|
|
371
|
+
| `error` | `ErrorEvent` | Protocol-level error |
|
|
372
|
+
|
|
373
|
+
Each payload is a Python dataclass — fields are typed, so editors autocomplete and type checkers catch typos.
|
|
374
|
+
|
|
375
|
+
## Multimodal cheat sheet
|
|
376
|
+
|
|
377
|
+
| Modality | Webhook stream | Knowledge ingest |
|
|
378
|
+
| ------------------------------- | ------------------------- | ------------------------------- |
|
|
379
|
+
| `text` | ✅ | ✅ (`ingest_text`) |
|
|
380
|
+
| `image` (URL) | ✅ (`image_url_message`) | extract OCR text first |
|
|
381
|
+
| `image` (pre-uploaded mediaKey) | ✅ (`media_key_message`) | — |
|
|
382
|
+
| `image-multi` | ✅ | — |
|
|
383
|
+
| `audio` (mediaKey) | ✅ | transcribe first, ingest text |
|
|
384
|
+
| `document` (mediaKey) | ✅ | extract text first, ingest text |
|
|
385
|
+
| `form-response` | ✅ | — |
|
|
386
|
+
| Knowledge Base search | ✅ via flow tool | ✅ (`KnowledgeSession.search`) |
|
|
387
|
+
|
|
388
|
+
The Python SDK does not handle file extraction (PDF → text, image → OCR, audio → transcript). Use whatever tool fits your stack (Azure Document Intelligence, AssemblyAI, Tesseract, etc.) and feed the resulting text into `KnowledgeSession.ingest_text()`. The Daguito flow's `search_knowledge_base` tool then surfaces it to the model.
|
|
389
|
+
|
|
390
|
+
## Testing from a script
|
|
391
|
+
|
|
392
|
+
The fastest way to verify your install works is to point the streaming session at any flow and dump events to stdout:
|
|
393
|
+
|
|
394
|
+
```python
|
|
395
|
+
import asyncio
|
|
396
|
+
import os
|
|
397
|
+
from daguito import WebhookStreamSession, WebhookStreamOptions, text_message
|
|
398
|
+
|
|
399
|
+
async def main():
|
|
400
|
+
opts = WebhookStreamOptions(
|
|
401
|
+
api_url=os.environ["DAGUITO_API_URL"],
|
|
402
|
+
webhook_id=os.environ["DAGUITO_WEBHOOK_ID"],
|
|
403
|
+
token=os.environ["DAGUITO_WEBHOOK_TOKEN"],
|
|
404
|
+
)
|
|
405
|
+
async with WebhookStreamSession(opts) as session:
|
|
406
|
+
await session.send(text_message("ping"))
|
|
407
|
+
async for event_type, payload in session.events():
|
|
408
|
+
print(event_type, payload)
|
|
409
|
+
if event_type in ("flow.completed", "flow.failed"):
|
|
410
|
+
break
|
|
411
|
+
|
|
412
|
+
asyncio.run(main())
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
DAGUITO_API_URL=https://api.daguito.com \
|
|
417
|
+
DAGUITO_WEBHOOK_ID=wh_abc123 \
|
|
418
|
+
DAGUITO_WEBHOOK_TOKEN=sk_wh_... \
|
|
419
|
+
python smoke.py
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Typing
|
|
423
|
+
|
|
424
|
+
Every public symbol has full type hints, including the dataclass payloads emitted by streaming events. The package ships a `py.typed` marker so `mypy` / `pyright` pick the types up automatically.
|
|
425
|
+
|
|
426
|
+
```python
|
|
427
|
+
from daguito import (
|
|
428
|
+
WebhookStreamSession,
|
|
429
|
+
WebhookStreamOptions,
|
|
430
|
+
NodeTokenEvent,
|
|
431
|
+
FlowCompletedEvent,
|
|
432
|
+
)
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
## Resources
|
|
436
|
+
|
|
437
|
+
- 🌐 [daguito.com](https://daguito.com) — landing & dashboard
|
|
438
|
+
- 📚 [docs.daguito.com](https://docs.daguito.com) — full API and flow reference
|
|
439
|
+
- 💬 [TypeScript SDK](https://github.com/daguitocontact-lang/js-sdk) — same surface, different runtime
|
|
440
|
+
- 🐛 [Issues](https://github.com/daguitocontact-lang/daguito-python/issues) — bug reports & feature requests
|
|
441
|
+
- 📦 [Source](https://github.com/daguitocontact-lang/daguito-python) — Python SDK repo
|
|
442
|
+
|
|
443
|
+
## License
|
|
444
|
+
|
|
445
|
+
MIT © [Daguito, LLC](https://daguito.com)
|