opencode-skills-collection 3.1.10 → 3.1.11

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.
Files changed (66) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +8 -1
  2. package/bundled-skills/browser-testing-with-devtools/SKILL.md +334 -0
  3. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  4. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  5. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  6. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  7. package/bundled-skills/docs/sources/sources.md +4 -0
  8. package/bundled-skills/docs/users/bundles.md +1 -1
  9. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  10. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  11. package/bundled-skills/docs/users/getting-started.md +1 -1
  12. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  13. package/bundled-skills/docs/users/usage.md +4 -4
  14. package/bundled-skills/docs/users/visual-guide.md +4 -4
  15. package/bundled-skills/drizzle-migration-conflict/SKILL.md +179 -0
  16. package/bundled-skills/drizzle-migration-conflict/references/ci-policy.md +87 -0
  17. package/bundled-skills/drizzle-migration-conflict/references/conflict-resolution.md +163 -0
  18. package/bundled-skills/drizzle-migration-conflict/references/report-template.md +69 -0
  19. package/bundled-skills/drizzle-migration-conflict/references/sources.md +51 -0
  20. package/bundled-skills/drizzle-migration-conflict/scripts/check_drizzle_migrations.py +721 -0
  21. package/bundled-skills/frontend-lighthouse/SKILL.md +348 -0
  22. package/bundled-skills/pre-release-review/SKILL.md +198 -0
  23. package/bundled-skills/pre-release-review/references/checklist.md +104 -0
  24. package/bundled-skills/pre-release-review/references/report-template.md +91 -0
  25. package/bundled-skills/re-create/SKILL.md +251 -0
  26. package/bundled-skills/weaviate/SKILL.md +132 -0
  27. package/bundled-skills/weaviate/references/ask.md +36 -0
  28. package/bundled-skills/weaviate/references/create_collection.md +152 -0
  29. package/bundled-skills/weaviate/references/environment_requirements.md +34 -0
  30. package/bundled-skills/weaviate/references/example_data.md +24 -0
  31. package/bundled-skills/weaviate/references/explore_collection.md +50 -0
  32. package/bundled-skills/weaviate/references/fetch_filter.md +88 -0
  33. package/bundled-skills/weaviate/references/get_collection.md +32 -0
  34. package/bundled-skills/weaviate/references/hybrid_search.md +47 -0
  35. package/bundled-skills/weaviate/references/import_data.md +160 -0
  36. package/bundled-skills/weaviate/references/keyword_search.md +38 -0
  37. package/bundled-skills/weaviate/references/list_collections.md +31 -0
  38. package/bundled-skills/weaviate/references/query_search.md +38 -0
  39. package/bundled-skills/weaviate/references/semantic_search.md +46 -0
  40. package/bundled-skills/weaviate/scripts/ask.py +106 -0
  41. package/bundled-skills/weaviate/scripts/create_collection.py +359 -0
  42. package/bundled-skills/weaviate/scripts/example_data.py +945 -0
  43. package/bundled-skills/weaviate/scripts/explore_collection.py +295 -0
  44. package/bundled-skills/weaviate/scripts/fetch_filter.py +261 -0
  45. package/bundled-skills/weaviate/scripts/get_collection.py +122 -0
  46. package/bundled-skills/weaviate/scripts/hybrid_search.py +157 -0
  47. package/bundled-skills/weaviate/scripts/import.py +701 -0
  48. package/bundled-skills/weaviate/scripts/keyword_search.py +142 -0
  49. package/bundled-skills/weaviate/scripts/list_collections.py +77 -0
  50. package/bundled-skills/weaviate/scripts/query_search.py +135 -0
  51. package/bundled-skills/weaviate/scripts/semantic_search.py +139 -0
  52. package/bundled-skills/weaviate/scripts/weaviate_conn.py +231 -0
  53. package/bundled-skills/weaviate-cookbooks/SKILL.md +67 -0
  54. package/bundled-skills/weaviate-cookbooks/references/advanced_rag.md +274 -0
  55. package/bundled-skills/weaviate-cookbooks/references/agentic_rag.md +360 -0
  56. package/bundled-skills/weaviate-cookbooks/references/async_client.md +428 -0
  57. package/bundled-skills/weaviate-cookbooks/references/basic_agent.md +270 -0
  58. package/bundled-skills/weaviate-cookbooks/references/basic_rag.md +219 -0
  59. package/bundled-skills/weaviate-cookbooks/references/data_explorer.md +336 -0
  60. package/bundled-skills/weaviate-cookbooks/references/environment_requirements.md +78 -0
  61. package/bundled-skills/weaviate-cookbooks/references/frontend_interface.md +104 -0
  62. package/bundled-skills/weaviate-cookbooks/references/pdf_multimodal_rag.md +635 -0
  63. package/bundled-skills/weaviate-cookbooks/references/project_setup.md +75 -0
  64. package/bundled-skills/weaviate-cookbooks/references/query_agent_chatbot.md +163 -0
  65. package/package.json +1 -1
  66. package/skills_index.json +156 -0
@@ -0,0 +1,428 @@
1
+ # Async Client Usage
2
+
3
+ Guide for using the Weaviate Python async client in production applications (FastAPI, async frameworks).
4
+
5
+ ## 📚 Official Documentation Reference
6
+
7
+ **For agents:** If you encounter any issues not covered here, refer to the official Weaviate documentation:
8
+
9
+ - **Primary Reference**: [Weaviate Async API Documentation](https://docs.weaviate.io/weaviate/client-libraries/python/async)
10
+ - **Python Client Reference**: [Weaviate Python Client Docs](https://docs.weaviate.io/weaviate/client-libraries/python)
11
+ - **API Reference**: [ReadTheDocs - Python Client](https://weaviate-python-client.readthedocs.io/en/stable/)
12
+ - **Troubleshooting**: [Community Forum](https://forum.weaviate.io/) | [GitHub Issues](https://github.com/weaviate/weaviate-python-client/issues)
13
+
14
+ > **Note**: The async client (`WeaviateAsyncClient`) is available in `weaviate-client` v4.7.0+.
15
+
16
+ ## Connection Methods
17
+
18
+ Three instantiation helpers are provided ([docs](https://docs.weaviate.io/weaviate/client-libraries/python/async#instantiation)):
19
+
20
+ ### Weaviate Cloud (Recommended)
21
+
22
+ ```python
23
+ import weaviate
24
+ from weaviate.classes.init import Auth
25
+
26
+ # Use the official helper function for Weaviate Cloud
27
+ client = weaviate.use_async_with_weaviate_cloud(
28
+ cluster_url="your-cluster.weaviate.cloud", # Accepts hostname with or without https://
29
+ auth_credentials=Auth.api_key("your-api-key"),
30
+ headers={ # Note: parameter is "headers" not "additional_headers"
31
+ "X-OpenAI-Api-Key": "sk-...",
32
+ "X-Anthropic-Api-Key": "sk-ant-...",
33
+ }
34
+ )
35
+
36
+ await client.connect() # Required! Async helpers don't auto-connect
37
+ ```
38
+
39
+ **Reference**: [Weaviate Cloud Setup](https://docs.weaviate.io/weaviate/quickstart)
40
+
41
+ ### Self-Hosted
42
+
43
+ ```python
44
+ # For local instances
45
+ client = weaviate.use_async_with_local()
46
+
47
+ # For custom endpoints
48
+ client = weaviate.use_async_with_custom(
49
+ http_host="localhost",
50
+ http_port=8080,
51
+ http_secure=False,
52
+ grpc_host="localhost",
53
+ grpc_port=50051,
54
+ grpc_secure=False,
55
+ )
56
+
57
+ await client.connect()
58
+ ```
59
+
60
+ **Reference**: [Connection Configuration](https://weaviate-python-client.readthedocs.io/en/stable/weaviate.html)
61
+
62
+ ### Authentication
63
+
64
+ Multiple authentication modes are supported ([docs](https://docs.weaviate.io/weaviate/client-libraries/python#authentication)):
65
+
66
+ ```python
67
+ from weaviate.classes.init import Auth
68
+
69
+ # API Key (most common for Weaviate Cloud)
70
+ auth = Auth.api_key("your-api-key")
71
+
72
+ # Bearer Token (with optional refresh token)
73
+ auth = Auth.bearer_token("access-token", refresh_token="refresh-token")
74
+
75
+ # Client Credentials (OIDC)
76
+ auth = Auth.client_credentials(client_secret="secret")
77
+
78
+ # Client Password (OIDC Resource Owner Password flow)
79
+ auth = Auth.client_password(username="user", password="pass")
80
+
81
+ # Usage
82
+ client = weaviate.use_async_with_weaviate_cloud(
83
+ cluster_url="your-cluster.weaviate.cloud",
84
+ auth_credentials=auth,
85
+ )
86
+ ```
87
+
88
+ ## Critical Patterns
89
+
90
+ ### ⚠️ Connection Lifecycle
91
+
92
+ **Important**: Unlike synchronous helpers, async helpers **do not connect automatically** ([docs](https://docs.weaviate.io/weaviate/client-libraries/python/async#instantiation)). You must explicitly call `.connect()` and `.close()`:
93
+
94
+ ```python
95
+ # ❌ Wrong - client not connected
96
+ client = weaviate.use_async_with_weaviate_cloud(...)
97
+ collections = await client.collections.list_all() # Will fail!
98
+
99
+ # ✅ Correct - explicit connect/close
100
+ client = weaviate.use_async_with_weaviate_cloud(...)
101
+ await client.connect()
102
+ collections = await client.collections.list_all()
103
+ await client.close()
104
+ ```
105
+
106
+ ### ⚠️ Sync vs Async Methods
107
+
108
+ **Key distinction** ([docs](https://docs.weaviate.io/weaviate/client-libraries/python/async#which-methods-are-async)): Methods involving server requests are async; local operations are synchronous.
109
+
110
+ ```python
111
+ # Collection retrieval is SYNC (no await)
112
+ collection = client.collections.get("MyCollection")
113
+
114
+ # Operations on collections are ASYNC (need await)
115
+ config = await collection.config.get()
116
+ results = await collection.query.fetch_objects()
117
+ count = await collection.aggregate.over_all()
118
+ ```
119
+
120
+ **Rule:** Getting the collection object is sync; calling methods on it is async.
121
+
122
+ ### ⚠️ Bulk Operations
123
+
124
+ **Important Note** ([docs](https://docs.weaviate.io/weaviate/client-libraries/python/async#bulk-import-operations)): For large-scale data imports, use the **synchronous client** and its batch operations. The sync client's batch methods already handle concurrency internally and are optimized for bulk operations.
125
+
126
+ ```python
127
+ # ✅ For bulk imports, prefer sync client
128
+ import weaviate
129
+
130
+ with weaviate.connect_to_weaviate_cloud(...) as client:
131
+ collection = client.collections.get("MyCollection")
132
+
133
+ # Batch insert handles concurrency automatically
134
+ with collection.batch.dynamic() as batch:
135
+ for item in large_dataset:
136
+ batch.add_object(properties=item)
137
+ ```
138
+
139
+ Use the async client for:
140
+
141
+ - Web applications (FastAPI, Starlette)
142
+ - Concurrent request handling
143
+ - Interactive queries
144
+
145
+ Don't use the async client for:
146
+
147
+ - Bulk data imports (use sync client instead)
148
+
149
+ ## Context Manager Pattern (Recommended)
150
+
151
+ **Best Practice** ([docs](https://docs.weaviate.io/weaviate/client-libraries/python/async#using-the-async-context-manager)): Use `async with` to automatically connect/disconnect:
152
+
153
+ ```python
154
+ from contextlib import asynccontextmanager
155
+ from typing import AsyncGenerator
156
+
157
+ @asynccontextmanager
158
+ async def get_weaviate_client(
159
+ cluster_url: str,
160
+ api_key: str,
161
+ provider_headers: dict[str, str] | None = None,
162
+ ) -> AsyncGenerator[weaviate.WeaviateAsyncClient, None]:
163
+ """Connect to Weaviate Cloud with automatic cleanup."""
164
+ # Remove scheme if present
165
+ hostname = cluster_url.replace("https://", "").replace("http://", "")
166
+
167
+ client = weaviate.use_async_with_weaviate_cloud(
168
+ cluster_url=hostname,
169
+ auth_credentials=Auth.api_key(api_key),
170
+ headers=provider_headers,
171
+ )
172
+
173
+ try:
174
+ await client.connect()
175
+ yield client
176
+ finally:
177
+ await client.close()
178
+
179
+ # Usage
180
+ async def example():
181
+ async with get_weaviate_client(
182
+ cluster_url="your-cluster.weaviate.cloud",
183
+ api_key="your-key",
184
+ ) as client:
185
+ collections = await client.collections.list_all()
186
+ ```
187
+
188
+ > **Note**: When using the context manager, `.connect()` and `.close()` are called automatically.
189
+
190
+ ## FastAPI Integration
191
+
192
+ **Use Case** ([docs](https://docs.weaviate.io/weaviate/client-libraries/python/async#use-cases)): The async client excels in web frameworks like FastAPI for handling concurrent requests.
193
+
194
+ Use lifespan management for shared client across requests:
195
+
196
+ ```python
197
+ from fastapi import FastAPI
198
+ from contextlib import asynccontextmanager
199
+
200
+ @asynccontextmanager
201
+ async def lifespan(app: FastAPI):
202
+ # Startup: connect to Weaviate
203
+ app.state.weaviate = weaviate.use_async_with_weaviate_cloud(
204
+ cluster_url="your-cluster.weaviate.cloud",
205
+ auth_credentials=Auth.api_key("your-key"),
206
+ )
207
+ await app.state.weaviate.connect()
208
+
209
+ yield
210
+
211
+ # Shutdown: close connection
212
+ await app.state.weaviate.close()
213
+
214
+ app = FastAPI(lifespan=lifespan)
215
+
216
+ @app.get("/collections")
217
+ async def list_collections():
218
+ collections = await app.state.weaviate.collections.list_all()
219
+ return {"collections": list(collections.keys())}
220
+ ```
221
+
222
+ **Community Discussion**: [FastAPI Best Practices](https://forum.weaviate.io/t/what-is-the-best-practice-to-use-v4-python-client-for-query-with-fastapi-or-other-async-python-framework/1245)
223
+
224
+ ## Common Pitfalls
225
+
226
+ ### 1. Parameter Name Confusion
227
+
228
+ ```python
229
+ # ❌ Wrong - WeaviateAsyncClient() constructor uses different param
230
+ client = weaviate.use_async_with_weaviate_cloud(
231
+ additional_headers={...} # Wrong parameter name!
232
+ )
233
+
234
+ # ✅ Correct - use "headers" not "additional_headers"
235
+ client = weaviate.use_async_with_weaviate_cloud(
236
+ headers={...}
237
+ )
238
+ ```
239
+
240
+ ### 2. URL Format
241
+
242
+ Both formats work with helper functions:
243
+
244
+ ```python
245
+ # ✅ Both accepted
246
+ client = weaviate.use_async_with_weaviate_cloud(
247
+ cluster_url="https://cluster.weaviate.cloud" # With scheme
248
+ )
249
+
250
+ client = weaviate.use_async_with_weaviate_cloud(
251
+ cluster_url="cluster.weaviate.cloud" # Without scheme
252
+ )
253
+ ```
254
+
255
+ ### 3. Sync vs Async Function Names
256
+
257
+ ```python
258
+ # ❌ Wrong - sync client (cannot use await)
259
+ client = weaviate.connect_to_weaviate_cloud(...)
260
+ await client.connect() # TypeError!
261
+
262
+ # ✅ Correct - async client
263
+ client = weaviate.use_async_with_weaviate_cloud(...)
264
+ await client.connect()
265
+ ```
266
+
267
+ **Naming pattern:**
268
+
269
+ - Sync: `connect_to_*` (e.g., `connect_to_weaviate_cloud`)
270
+ - Async: `use_async_with_*` (e.g., `use_async_with_weaviate_cloud`)
271
+
272
+ ### 4. Port Configuration
273
+
274
+ ```python
275
+ # ❌ Wrong - manual port config causes conflicts with Weaviate Cloud
276
+ client = WeaviateAsyncClient(
277
+ connection_params=ConnectionParams.from_url(
278
+ url="https://cluster.weaviate.cloud",
279
+ grpc_port=443, # Conflict!
280
+ )
281
+ )
282
+
283
+ # ✅ Correct - use helper function (handles ports automatically)
284
+ client = weaviate.use_async_with_weaviate_cloud(
285
+ cluster_url="cluster.weaviate.cloud"
286
+ )
287
+ ```
288
+
289
+ **Rule:** For Weaviate Cloud, always use `use_async_with_weaviate_cloud()` — it handles HTTP (443) and gRPC (50051) ports correctly.
290
+
291
+ ## Multi-Cluster Example
292
+
293
+ Managing connections to multiple Weaviate clusters:
294
+
295
+ ```python
296
+ @asynccontextmanager
297
+ async def get_multi_cluster_clients(
298
+ clusters: dict[str, dict[str, str]]
299
+ ) -> AsyncGenerator[dict[str, weaviate.WeaviateAsyncClient], None]:
300
+ """Connect to multiple Weaviate clusters.
301
+
302
+ Args:
303
+ clusters: Dict of {cluster_id: {"url": "...", "api_key": "..."}}
304
+ """
305
+ clients = {}
306
+
307
+ try:
308
+ # Connect to all clusters
309
+ for cluster_id, config in clusters.items():
310
+ client = weaviate.use_async_with_weaviate_cloud(
311
+ cluster_url=config["url"],
312
+ auth_credentials=Auth.api_key(config["api_key"]),
313
+ )
314
+ await client.connect()
315
+ clients[cluster_id] = client
316
+
317
+ yield clients
318
+
319
+ finally:
320
+ # Close all connections
321
+ for client in clients.values():
322
+ await client.close()
323
+
324
+ # Usage
325
+ async def example():
326
+ clusters = {
327
+ "prod": {"url": "prod.weaviate.cloud", "api_key": "key1"},
328
+ "dev": {"url": "dev.weaviate.cloud", "api_key": "key2"},
329
+ }
330
+
331
+ async with get_multi_cluster_clients(clusters) as clients:
332
+ prod_collections = await clients["prod"].collections.list_all()
333
+ dev_collections = await clients["dev"].collections.list_all()
334
+ ```
335
+
336
+ ## Environment Variables
337
+
338
+ See [Environment Requirements](environment_requirements.md) for provider API keys.
339
+
340
+ ```python
341
+ import os
342
+
343
+ # Read from environment
344
+ cluster_url = os.environ["WEAVIATE_URL"]
345
+ api_key = os.environ["WEAVIATE_API_KEY"]
346
+
347
+ # Build provider headers
348
+ provider_headers = {}
349
+ if openai_key := os.getenv("OPENAI_API_KEY"):
350
+ provider_headers["X-OpenAI-Api-Key"] = openai_key
351
+ if anthropic_key := os.getenv("ANTHROPIC_API_KEY"):
352
+ provider_headers["X-Anthropic-Api-Key"] = anthropic_key
353
+
354
+ client = weaviate.use_async_with_weaviate_cloud(
355
+ cluster_url=cluster_url,
356
+ auth_credentials=Auth.api_key(api_key),
357
+ headers=provider_headers or None,
358
+ )
359
+ ```
360
+
361
+ ## Testing Async Code
362
+
363
+ ```python
364
+ import pytest
365
+
366
+ @pytest.mark.asyncio
367
+ async def test_weaviate_connection():
368
+ async with get_weaviate_client(
369
+ cluster_url="test-cluster.weaviate.cloud",
370
+ api_key="test-key",
371
+ ) as client:
372
+ collections = await client.collections.list_all()
373
+ assert isinstance(collections, dict)
374
+ ```
375
+
376
+ ## Quick Reference
377
+
378
+ | Task | Pattern | Await? |
379
+ | ---------------- | --------------------------------------------- | ------- |
380
+ | Create client | `weaviate.use_async_with_weaviate_cloud(...)` | No |
381
+ | Connect | `client.connect()` | **Yes** |
382
+ | Get collection | `client.collections.get("Name")` | No |
383
+ | List collections | `client.collections.list_all()` | **Yes** |
384
+ | Query data | `collection.query.fetch_objects()` | **Yes** |
385
+ | Get config | `collection.config.get()` | **Yes** |
386
+ | Aggregate | `collection.aggregate.over_all()` | **Yes** |
387
+ | Close | `client.close()` | **Yes** |
388
+
389
+ ## Troubleshooting
390
+
391
+ ### Common Issues
392
+
393
+ | Issue | Solution | Reference |
394
+ | ----------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------- |
395
+ | Connection hangs indefinitely | Use context manager or ensure proper `.close()` | [GitHub #753](https://github.com/weaviate/weaviate-python-client/issues/753) |
396
+ | Multi-worker conflicts (Gunicorn) | Use lifespan management, not startup hooks | [GitHub #1292](https://github.com/weaviate/weaviate-python-client/issues/1292) |
397
+ | `TypeError: object NoneType can't be used in 'await'` | Use `use_async_with_*` not `connect_to_*` | [Async API Docs](https://docs.weaviate.io/weaviate/client-libraries/python/async) |
398
+ | Port conflicts with Weaviate Cloud | Use helper functions, not manual `ConnectionParams` | See "Common Pitfalls #4" above |
399
+
400
+ ### Getting Help
401
+
402
+ **For agents:** When encountering errors:
403
+
404
+ 1. Check the [Common Pitfalls](#common-pitfalls) section above
405
+ 2. Search [Community Forum](https://forum.weaviate.io/) for similar issues
406
+ 3. Check [GitHub Issues](https://github.com/weaviate/weaviate-python-client/issues) for known bugs
407
+ 4. Refer to [official async documentation](https://docs.weaviate.io/weaviate/client-libraries/python/async)
408
+ 5. Review [Python client best practices](https://docs.weaviate.io/weaviate/client-libraries/python/notes-best-practices)
409
+
410
+ ## Additional Resources
411
+
412
+ ### Official Documentation
413
+
414
+ - **Primary**: [Weaviate Async API](https://docs.weaviate.io/weaviate/client-libraries/python/async)
415
+ - **Python Client**: [Main Documentation](https://docs.weaviate.io/weaviate/client-libraries/python)
416
+ - **API Reference**: [ReadTheDocs](https://weaviate-python-client.readthedocs.io/en/stable/)
417
+ - **Best Practices**: [Notes and Best Practices](https://docs.weaviate.io/weaviate/client-libraries/python/notes-best-practices)
418
+
419
+ ### Framework Integration
420
+
421
+ - [FastAPI Lifespan Events](https://fastapi.tiangolo.com/advanced/events/)
422
+ - [Python Async Context Managers](https://docs.python.org/3/reference/datamodel.html#asynchronous-context-managers)
423
+
424
+ ### Community
425
+
426
+ - [Weaviate Community Forum](https://forum.weaviate.io/)
427
+ - [Python Client GitHub](https://github.com/weaviate/weaviate-python-client)
428
+ - [Weaviate Blog](https://weaviate.io/blog)
@@ -0,0 +1,270 @@
1
+ # Basic Agent Cookbook
2
+
3
+ Build a tool-calling AI agent with structured outputs using DSPy.
4
+ For RAG tools, memory, and framework integrations, [see here](./agentic_rag.md).
5
+
6
+
7
+
8
+ Docs to reference if needed:
9
+ - DSPy signatures: https://dspy.ai/learn/programming/signatures/
10
+ - DSPy language models: https://dspy.ai/learn/programming/language_models/
11
+ - LiteLLM providers: https://docs.litellm.ai/docs/
12
+
13
+ ## Core Rules
14
+
15
+ - Use a virtual environment via `venv`
16
+ - Use `uv` for Python project/dependency management.
17
+ - Do not manually author `pyproject.toml` or `uv.lock`; let `uv` generate/update them.
18
+ - Use this install set: `uv add dspy python-dotenv`
19
+ - Customise this cookbook to the users specification, ask them for details if not given.
20
+
21
+ ## Env Rules
22
+
23
+ Mandatory:
24
+ - An LLM provider API key (e.g. `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`)
25
+ - `WEAVIATE_URL`
26
+ - `WEAVIATE_API_KEY`
27
+
28
+ Optional:
29
+ - Matching provider keys listed in `environment_requirements.md`
30
+
31
+ If the user explicitly requests a non-Weaviate agent, `WEAVIATE_URL` and `WEAVIATE_API_KEY` can be omitted.
32
+
33
+ ## Agent Response Signature
34
+
35
+ The structured output that defines what the LLM returns when selecting tools.
36
+
37
+ ```python
38
+ import dspy
39
+ from typing import Any, Dict
40
+
41
+ class AgentResponse(dspy.Signature):
42
+
43
+ # Input Fields
44
+ history: dspy.History = dspy.InputField()
45
+ user_prompt: str = dspy.InputField()
46
+ available_tools: str = dspy.InputField()
47
+
48
+ # Output Fields
49
+ response: str = dspy.OutputField(
50
+ description="The response to the user's prompt whilst the tool is running. Update the user on the progress of their request (if a tool is picked), or the final response to the user (if no tool is picked)."
51
+ )
52
+ tool: str | None = dspy.OutputField(
53
+ description="The tool that needs to be used. Return None if no tool is needed."
54
+ )
55
+ tool_inputs: Dict[str, Any] | None = dspy.OutputField(
56
+ description=(
57
+ "The inputs for the tool. Return an empty dictionary (still include the field) if no inputs are needed. "
58
+ "The key is the name of the input, the value is the value of the input."
59
+ )
60
+ )
61
+ ```
62
+
63
+ Extend `AgentResponse` as needed: add `confidence: float` for certainty scoring, `requires_clarification: bool` for follow-up questions, or modify `description` strings to shape agent behaviour for a specific domain.
64
+
65
+ ## Router Agent (Single Step)
66
+
67
+ Wraps the agent response into a class that manages conversation history and tool execution.
68
+
69
+ ```python
70
+ from typing import Callable, List, Tuple
71
+
72
+ class RouterAgent:
73
+ def __init__(self, model: str, tools: List[Callable] = []):
74
+ self.tools: list[Callable] = tools
75
+ self.model = dspy.LM(model)
76
+ self.agent = dspy.ChainOfThought(AgentResponse)
77
+ self.conversation_history = dspy.History(messages=[])
78
+
79
+ def add_conversation_history(self, message: str, response: dspy.Prediction):
80
+ self.conversation_history.messages.append({"user_prompt": message, **response})
81
+
82
+ def get_tools_and_descriptions(self) -> str:
83
+ return "\n".join(
84
+ [
85
+ f"{tool.__name__}:\nDescription: {tool.__doc__ or ''}\nInputs: { {k: v for k, v in tool.__annotations__.items() if k != 'return'} }"
86
+ for tool in self.tools
87
+ ]
88
+ )
89
+
90
+ def get_response(self, user_prompt: str) -> Tuple[str, str | None]:
91
+ result = self.agent(
92
+ history=self.conversation_history,
93
+ user_prompt=user_prompt,
94
+ available_tools=self.get_tools_and_descriptions(),
95
+ lm=self.model,
96
+ )
97
+ self.add_conversation_history(message=user_prompt, response=result)
98
+ if result.tool and result.tool.lower() not in ["null", "none"]:
99
+ tool_function = next(
100
+ (tool for tool in self.tools if tool.__name__ == result.tool), None
101
+ )
102
+ if tool_function is None:
103
+ raise ValueError(f"Tool {result.tool} not found")
104
+ tool_inputs = {k: v for k, v in result.tool_inputs.items() if k != "return"}
105
+ tool_result = tool_function(**tool_inputs)
106
+ else:
107
+ tool_result = None
108
+ return result.response, tool_result
109
+ ```
110
+
111
+ Usage:
112
+
113
+ ```python
114
+ router = RouterAgent(
115
+ model="<model_name>", # e.g. claude-sonnet-4-5, gpt-5.2, gemini-2.5-pro
116
+ tools=[your_tool_function]
117
+ )
118
+ response, tool_result = router.get_response("user query here")
119
+ ```
120
+
121
+ ## Tool Design
122
+
123
+ Tools are Python functions. The agent reads `__name__`, `__doc__`, and `__annotations__` to decide when to use them.
124
+
125
+ ```python
126
+ def your_tool(param1: str, param2: int) -> str:
127
+ """Clear description of what this tool does and when to use it."""
128
+ # tool logic here
129
+ return "result as string"
130
+ ```
131
+
132
+ Key rules:
133
+ - Docstrings directly influence when the agent selects the tool. Be specific: "Get current weather conditions for a city" is better than "Get weather".
134
+ - Type hints guide what inputs the agent provides. Complex types like `filters: List[Dict]` may need additional description in the docstring.
135
+ - Return strings or string-serializable data.
136
+
137
+
138
+ ## Sequential Multi-Step Agent
139
+
140
+ For tasks requiring multiple tool calls in succession, add a followup signature and loop.
141
+
142
+ Followup signature (receives `tool_output` from the previous step):
143
+
144
+ ```python
145
+ class AgentFollowup(dspy.Signature):
146
+
147
+ # Input Fields
148
+ history: dspy.History = dspy.InputField()
149
+ user_prompt: str = dspy.InputField()
150
+ tool_output: str = dspy.InputField(description="The output of the previous tool.")
151
+ available_tools: str = dspy.InputField(
152
+ description="The available tools and their descriptions."
153
+ )
154
+
155
+ # Output Fields
156
+ response: str = dspy.OutputField(
157
+ description="The response to the user's prompt whilst the tool is running. Update the user on the progress of their request (if a tool is picked), or the final response to the user (if no tool is picked)."
158
+ )
159
+ tool: str | None = dspy.OutputField(
160
+ description="The tool that needs to be used. Return None if no tool is needed."
161
+ )
162
+ tool_inputs: Dict[str, Any] | None = dspy.OutputField(
163
+ description="The inputs for the tool. Return an empty dictionary (still include the field) if no inputs are needed. The key is the name of the input, the value is the value of the input.",
164
+ )
165
+ ```
166
+
167
+ Modify `RouterAgent` to loop until the agent stops requesting tools:
168
+
169
+ ```python
170
+ class RouterAgent:
171
+ def __init__(self, model: str, tools: List[Callable] = []):
172
+ self.tools: list[Callable] = tools
173
+ self.model = dspy.LM(model)
174
+ self.agent = dspy.ChainOfThought(AgentResponse)
175
+ self.followup_agent = dspy.ChainOfThought(AgentFollowup)
176
+ self.conversation_history = dspy.History(messages=[])
177
+
178
+ def add_conversation_history(self, message: str, response: dspy.Prediction):
179
+ self.conversation_history.messages.append({"user_prompt": message, **response})
180
+
181
+ def get_tools_and_descriptions(self) -> str:
182
+ return "\n".join(
183
+ [
184
+ f"{tool.__name__}:\nDescription: {tool.__doc__ or ''}\nInputs: { {k: v for k, v in tool.__annotations__.items() if k != 'return'} }"
185
+ for tool in self.tools
186
+ ]
187
+ )
188
+
189
+ def get_response(self, user_prompt: str) -> str:
190
+ result = self.agent(
191
+ history=self.conversation_history,
192
+ user_prompt=user_prompt,
193
+ available_tools=self.get_tools_and_descriptions(),
194
+ lm=self.model,
195
+ )
196
+ self.add_conversation_history(message=user_prompt, response=result)
197
+
198
+ max_iter = 10
199
+ iter = 0
200
+
201
+ while result.tool is not None and result.tool.lower() not in ["null", "none"]:
202
+ iter += 1
203
+ if iter > max_iter:
204
+ break
205
+
206
+ tool_function = next(
207
+ (tool for tool in self.tools if tool.__name__ == result.tool), None
208
+ )
209
+ if tool_function is None:
210
+ raise ValueError(f"Tool {result.tool} not found")
211
+
212
+ tool_inputs = {k: v for k, v in result.tool_inputs.items() if k != "return"}
213
+ tool_result = tool_function(**tool_inputs)
214
+
215
+ result = self.followup_agent(
216
+ history=self.conversation_history,
217
+ tool_output=tool_result,
218
+ available_tools=self.get_tools_and_descriptions(),
219
+ lm=self.model,
220
+ )
221
+ self.add_conversation_history(message=tool_result, response=result)
222
+
223
+ return result.response
224
+ ```
225
+
226
+ `max_iter` controls how many tool calls can occur before forced termination. Increase for complex multi-step tasks, decrease to limit costs and runaway loops.
227
+
228
+ ## User-specific Customisations
229
+
230
+ If not specified ask the user about these points before implementing their respective strategies:
231
+
232
+ **LLM Framework**
233
+
234
+ You can use DSPy (works with all LiteLLM providers) or LiteLLM itself.
235
+
236
+ - DSPy: https://dspy.ai/learn/programming/language_models/
237
+ - LiteLLM: https://docs.litellm.ai/docs/
238
+
239
+ Alternatively, users can use a single model provider. What model provider will they use?
240
+
241
+ - OpenAI (https://platform.openai.com/docs/libraries)
242
+ - Anthropic (https://platform.claude.com/docs/)
243
+ - Google GenAI (https://ai.google.dev/gemini-api/docs/libraries)
244
+ - Other (such as locally hosted models), use best judgement
245
+
246
+ These may require additional installs.
247
+
248
+ **Model Selection**
249
+
250
+ What model(s) will the user use? Consider a mixed approach: capable model for main agent routing, cheaper model for auxiliary tasks like memory creation.
251
+
252
+ **Tools**
253
+
254
+ What tools does the user need? List their functions, inputs, and expected outputs. The agent is only as capable as its tools.
255
+
256
+ **Single-step vs Multi-step**
257
+
258
+ Does the user need a single tool call per query, or should the agent chain multiple tools in sequence? Only use multi-step if the use case requires it.
259
+
260
+ ## Troubleshooting
261
+
262
+ - DSPy signature warnings about missing fields: ensure all input fields are passed or mark optional fields appropriately.
263
+ - Tool not found errors: ensure tool function names match exactly what the agent outputs.
264
+ - Agent loops indefinitely: lower `max_iter` or add more explicit termination conditions.
265
+ - For any other issues, refer to the official library/package documentation and use web search extensively for troubleshooting.
266
+
267
+ ## Done Criteria
268
+
269
+ - Create test scripts to check each function works independently with test data. Tear down tests after completion, or create a proper test suite with pytest (requires install)
270
+ - User has completed specification of the app.