webagents 0.1.12__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.
- webagents/__init__.py +18 -0
- webagents/agents/__init__.py +13 -0
- webagents/agents/core/__init__.py +19 -0
- webagents/agents/core/base_agent.py +1834 -0
- webagents/agents/core/handoffs.py +293 -0
- webagents/agents/handoffs/__init__.py +0 -0
- webagents/agents/interfaces/__init__.py +0 -0
- webagents/agents/lifecycle/__init__.py +0 -0
- webagents/agents/skills/__init__.py +109 -0
- webagents/agents/skills/base.py +136 -0
- webagents/agents/skills/core/__init__.py +8 -0
- webagents/agents/skills/core/guardrails/__init__.py +0 -0
- webagents/agents/skills/core/llm/__init__.py +0 -0
- webagents/agents/skills/core/llm/anthropic/__init__.py +1 -0
- webagents/agents/skills/core/llm/litellm/__init__.py +10 -0
- webagents/agents/skills/core/llm/litellm/skill.py +538 -0
- webagents/agents/skills/core/llm/openai/__init__.py +1 -0
- webagents/agents/skills/core/llm/xai/__init__.py +1 -0
- webagents/agents/skills/core/mcp/README.md +375 -0
- webagents/agents/skills/core/mcp/__init__.py +15 -0
- webagents/agents/skills/core/mcp/skill.py +731 -0
- webagents/agents/skills/core/memory/__init__.py +11 -0
- webagents/agents/skills/core/memory/long_term_memory/__init__.py +10 -0
- webagents/agents/skills/core/memory/long_term_memory/memory_skill.py +639 -0
- webagents/agents/skills/core/memory/short_term_memory/__init__.py +9 -0
- webagents/agents/skills/core/memory/short_term_memory/skill.py +341 -0
- webagents/agents/skills/core/memory/vector_memory/skill.py +447 -0
- webagents/agents/skills/core/planning/__init__.py +9 -0
- webagents/agents/skills/core/planning/planner.py +343 -0
- webagents/agents/skills/ecosystem/__init__.py +0 -0
- webagents/agents/skills/ecosystem/crewai/__init__.py +1 -0
- webagents/agents/skills/ecosystem/database/__init__.py +1 -0
- webagents/agents/skills/ecosystem/filesystem/__init__.py +0 -0
- webagents/agents/skills/ecosystem/google/__init__.py +0 -0
- webagents/agents/skills/ecosystem/google/calendar/__init__.py +6 -0
- webagents/agents/skills/ecosystem/google/calendar/skill.py +306 -0
- webagents/agents/skills/ecosystem/n8n/__init__.py +0 -0
- webagents/agents/skills/ecosystem/openai_agents/__init__.py +0 -0
- webagents/agents/skills/ecosystem/web/__init__.py +0 -0
- webagents/agents/skills/ecosystem/zapier/__init__.py +0 -0
- webagents/agents/skills/robutler/__init__.py +11 -0
- webagents/agents/skills/robutler/auth/README.md +63 -0
- webagents/agents/skills/robutler/auth/__init__.py +17 -0
- webagents/agents/skills/robutler/auth/skill.py +354 -0
- webagents/agents/skills/robutler/crm/__init__.py +18 -0
- webagents/agents/skills/robutler/crm/skill.py +368 -0
- webagents/agents/skills/robutler/discovery/README.md +281 -0
- webagents/agents/skills/robutler/discovery/__init__.py +16 -0
- webagents/agents/skills/robutler/discovery/skill.py +230 -0
- webagents/agents/skills/robutler/kv/__init__.py +6 -0
- webagents/agents/skills/robutler/kv/skill.py +80 -0
- webagents/agents/skills/robutler/message_history/__init__.py +9 -0
- webagents/agents/skills/robutler/message_history/skill.py +270 -0
- webagents/agents/skills/robutler/messages/__init__.py +0 -0
- webagents/agents/skills/robutler/nli/__init__.py +13 -0
- webagents/agents/skills/robutler/nli/skill.py +687 -0
- webagents/agents/skills/robutler/notifications/__init__.py +5 -0
- webagents/agents/skills/robutler/notifications/skill.py +141 -0
- webagents/agents/skills/robutler/payments/__init__.py +41 -0
- webagents/agents/skills/robutler/payments/exceptions.py +255 -0
- webagents/agents/skills/robutler/payments/skill.py +610 -0
- webagents/agents/skills/robutler/storage/__init__.py +10 -0
- webagents/agents/skills/robutler/storage/files/__init__.py +9 -0
- webagents/agents/skills/robutler/storage/files/skill.py +445 -0
- webagents/agents/skills/robutler/storage/json/__init__.py +9 -0
- webagents/agents/skills/robutler/storage/json/skill.py +336 -0
- webagents/agents/skills/robutler/storage/kv/skill.py +88 -0
- webagents/agents/skills/robutler/storage.py +389 -0
- webagents/agents/tools/__init__.py +0 -0
- webagents/agents/tools/decorators.py +426 -0
- webagents/agents/tracing/__init__.py +0 -0
- webagents/agents/workflows/__init__.py +0 -0
- webagents/api/__init__.py +17 -0
- webagents/api/client.py +1207 -0
- webagents/api/types.py +253 -0
- webagents/scripts/__init__.py +0 -0
- webagents/server/__init__.py +28 -0
- webagents/server/context/__init__.py +0 -0
- webagents/server/context/context_vars.py +121 -0
- webagents/server/core/__init__.py +0 -0
- webagents/server/core/app.py +843 -0
- webagents/server/core/middleware.py +69 -0
- webagents/server/core/models.py +98 -0
- webagents/server/core/monitoring.py +59 -0
- webagents/server/endpoints/__init__.py +0 -0
- webagents/server/interfaces/__init__.py +0 -0
- webagents/server/middleware.py +330 -0
- webagents/server/models.py +92 -0
- webagents/server/monitoring.py +659 -0
- webagents/utils/__init__.py +0 -0
- webagents/utils/logging.py +359 -0
- webagents-0.1.12.dist-info/METADATA +99 -0
- webagents-0.1.12.dist-info/RECORD +96 -0
- webagents-0.1.12.dist-info/WHEEL +4 -0
- webagents-0.1.12.dist-info/entry_points.txt +2 -0
- webagents-0.1.12.dist-info/licenses/LICENSE +1 -0
@@ -0,0 +1,447 @@
|
|
1
|
+
"""
|
2
|
+
VectorMemorySkill - Milvus-backed vector memory for agent instructions/context
|
3
|
+
|
4
|
+
Features:
|
5
|
+
- Store and retrieve instruction documents (common or agent-specific)
|
6
|
+
- Tool to fetch relevant instructions for a problem (common + agent-specific)
|
7
|
+
- Owner-only tools to upload/remove documents
|
8
|
+
|
9
|
+
Env configuration:
|
10
|
+
- MILVUS_HOST (e.g., https://in03)
|
11
|
+
- MILVUS_PORT (e.g., 443)
|
12
|
+
- MILVUS_TOKEN (if required by Milvus/Cosmos)
|
13
|
+
- MILVUS_COLLECTION (default: robutler_memory)
|
14
|
+
- MILVUS_FORCE_RECREATE (truthy to recreate collection on init)
|
15
|
+
- EMBEDDING_MODEL (default: text-embedding-3-small)
|
16
|
+
- LITELLM_BASE_URL (default: http://localhost:2225)
|
17
|
+
- LITELLM_API_KEY or ROBUTLER_API_KEY (bearer for embeddings)
|
18
|
+
"""
|
19
|
+
|
20
|
+
from __future__ import annotations
|
21
|
+
|
22
|
+
import os
|
23
|
+
import uuid
|
24
|
+
from typing import Any, Dict, List, Optional, Tuple
|
25
|
+
|
26
|
+
from webagents.agents.skills.base import Skill
|
27
|
+
from webagents.agents.tools.decorators import tool, prompt
|
28
|
+
from webagents.utils.logging import get_logger, log_skill_event, log_tool_execution
|
29
|
+
|
30
|
+
try:
|
31
|
+
from pymilvus import (
|
32
|
+
connections,
|
33
|
+
FieldSchema, CollectionSchema, DataType, Collection,
|
34
|
+
utility as milvus_utility,
|
35
|
+
)
|
36
|
+
_MILVUS_AVAILABLE = True
|
37
|
+
except Exception:
|
38
|
+
_MILVUS_AVAILABLE = False
|
39
|
+
|
40
|
+
|
41
|
+
def _is_truthy(val: Optional[str]) -> bool:
|
42
|
+
return str(val).lower() in {"1", "true", "yes", "y", "on"}
|
43
|
+
|
44
|
+
|
45
|
+
def _get_env_str(name: str, default: Optional[str] = None) -> Optional[str]:
|
46
|
+
v = os.getenv(name, default)
|
47
|
+
return v if v and str(v).strip() != "" else default
|
48
|
+
|
49
|
+
|
50
|
+
class VectorMemorySkill(Skill):
|
51
|
+
def __init__(self, config: Dict[str, Any] | None = None):
|
52
|
+
super().__init__(config or {}, scope="all")
|
53
|
+
self.config = config or {}
|
54
|
+
self.logger = None
|
55
|
+
self.collection: Optional[Collection] = None
|
56
|
+
self.dim: int = int(os.getenv("EMBEDDING_DIM", "1536"))
|
57
|
+
# Milvus config
|
58
|
+
self.milvus_host = _get_env_str("MILVUS_HOST", "http://localhost")
|
59
|
+
self.milvus_port = int(os.getenv("MILVUS_PORT", "19530"))
|
60
|
+
self.milvus_token = _get_env_str("MILVUS_TOKEN")
|
61
|
+
self.milvus_collection = _get_env_str("MILVUS_COLLECTION", "robutler_memory")
|
62
|
+
self.milvus_force_recreate = _is_truthy(os.getenv("MILVUS_FORCE_RECREATE", "false"))
|
63
|
+
# Embeddings config
|
64
|
+
self.embed_model = _get_env_str("EMBEDDING_MODEL", "text-embedding-3-small")
|
65
|
+
self.litellm_base = _get_env_str("LITELLM_BASE_URL", "http://localhost:2225")
|
66
|
+
self.litellm_key = _get_env_str("LITELLM_API_KEY") or _get_env_str("ROBUTLER_API_KEY")
|
67
|
+
|
68
|
+
async def initialize(self, agent) -> None:
|
69
|
+
self.agent = agent
|
70
|
+
self.logger = get_logger('skill.robutler.vector_memory', agent.name)
|
71
|
+
if not _MILVUS_AVAILABLE:
|
72
|
+
self.logger.warning("pymilvus not available; VectorMemorySkill disabled")
|
73
|
+
return
|
74
|
+
try:
|
75
|
+
connections.connect(
|
76
|
+
alias="default",
|
77
|
+
uri=self.milvus_host if self.milvus_host.startswith("http") else None,
|
78
|
+
host=None if self.milvus_host.startswith("http") else self.milvus_host,
|
79
|
+
port=str(self.milvus_port),
|
80
|
+
token=self.milvus_token,
|
81
|
+
)
|
82
|
+
except Exception as e:
|
83
|
+
self.logger.warning(f"Failed to connect to Milvus: {e}")
|
84
|
+
return
|
85
|
+
|
86
|
+
try:
|
87
|
+
if self.milvus_force_recreate and milvus_utility.has_collection(self.milvus_collection):
|
88
|
+
milvus_utility.drop_collection(self.milvus_collection)
|
89
|
+
|
90
|
+
if not milvus_utility.has_collection(self.milvus_collection):
|
91
|
+
fields = [
|
92
|
+
FieldSchema(name="id", dtype=DataType.VARCHAR, is_primary=True, max_length=64),
|
93
|
+
FieldSchema(name="agent_id", dtype=DataType.VARCHAR, max_length=64),
|
94
|
+
FieldSchema(name="owner_user_id", dtype=DataType.VARCHAR, max_length=64),
|
95
|
+
FieldSchema(name="title", dtype=DataType.VARCHAR, max_length=256),
|
96
|
+
FieldSchema(name="content", dtype=DataType.VARCHAR, max_length=8192),
|
97
|
+
FieldSchema(name="vector", dtype=DataType.FLOAT_VECTOR, dim=self.dim),
|
98
|
+
]
|
99
|
+
schema = CollectionSchema(fields=fields, description="Robutler Vector Memory")
|
100
|
+
self.collection = Collection(name=self.milvus_collection, schema=schema)
|
101
|
+
try:
|
102
|
+
self.collection.create_index(
|
103
|
+
field_name="vector",
|
104
|
+
index_params={
|
105
|
+
"index_type": "IVF_FLAT",
|
106
|
+
"metric_type": "IP",
|
107
|
+
"params": {"nlist": 1024},
|
108
|
+
},
|
109
|
+
)
|
110
|
+
except Exception:
|
111
|
+
pass
|
112
|
+
self.collection.load()
|
113
|
+
else:
|
114
|
+
self.collection = Collection(self.milvus_collection)
|
115
|
+
try:
|
116
|
+
self.collection.load()
|
117
|
+
except Exception:
|
118
|
+
pass
|
119
|
+
self.logger.info(f"VectorMemorySkill initialized; collection={self.milvus_collection}")
|
120
|
+
try:
|
121
|
+
log_skill_event(self.agent.name, 'vector_memory', 'initialized', {
|
122
|
+
'milvus_host': (self.milvus_host or '')[:40],
|
123
|
+
'milvus_port': self.milvus_port,
|
124
|
+
'collection': self.milvus_collection,
|
125
|
+
'force_recreate': self.milvus_force_recreate,
|
126
|
+
'embed_model': self.embed_model,
|
127
|
+
})
|
128
|
+
except Exception:
|
129
|
+
pass
|
130
|
+
except Exception as e:
|
131
|
+
self.logger.warning(f"Milvus collection init failed: {e}")
|
132
|
+
self.collection = None
|
133
|
+
|
134
|
+
async def _embed(self, texts: List[str]) -> List[List[float]]:
|
135
|
+
if not texts:
|
136
|
+
return []
|
137
|
+
try:
|
138
|
+
import httpx
|
139
|
+
headers = {"Content-Type": "application/json"}
|
140
|
+
if self.litellm_key:
|
141
|
+
headers["Authorization"] = f"Bearer {self.litellm_key}"
|
142
|
+
url = f"{self.litellm_base.rstrip('/')}/v1/embeddings"
|
143
|
+
payload = {"model": self.embed_model, "input": texts}
|
144
|
+
async with httpx.AsyncClient(timeout=20.0) as client:
|
145
|
+
resp = await client.post(url, json=payload, headers=headers)
|
146
|
+
resp.raise_for_status()
|
147
|
+
data = resp.json().get("data", [])
|
148
|
+
return [item.get("embedding", []) for item in data]
|
149
|
+
except Exception as e:
|
150
|
+
if self.logger:
|
151
|
+
self.logger.warning(f"Embedding failed: {e}")
|
152
|
+
return [[0.0] * self.dim for _ in texts]
|
153
|
+
|
154
|
+
def _ensure_collection(self) -> bool:
|
155
|
+
return bool(_MILVUS_AVAILABLE and self.collection)
|
156
|
+
|
157
|
+
@tool(description="Retrieve relevant instruction documents (common + agent-specific) for the current problem.")
|
158
|
+
async def fetch_instructions_tool(self, problem: str, top_k: int = 3, context: Any = None) -> str:
|
159
|
+
if not self._ensure_collection():
|
160
|
+
return "❌ Vector memory unavailable"
|
161
|
+
try:
|
162
|
+
agent_id = getattr(self.agent, 'id', None)
|
163
|
+
vectors = await self._embed([problem])
|
164
|
+
if not vectors:
|
165
|
+
return ""
|
166
|
+
qvec = vectors[0]
|
167
|
+
expr = "agent_id == ''"
|
168
|
+
if agent_id:
|
169
|
+
expr = f"({expr}) or agent_id == '{agent_id}'"
|
170
|
+
res = self.collection.search(
|
171
|
+
data=[qvec],
|
172
|
+
anns_field="vector",
|
173
|
+
param={"nprobe": 16},
|
174
|
+
limit=max(1, int(top_k)),
|
175
|
+
output_fields=["id", "title", "content", "agent_id"],
|
176
|
+
expr=expr,
|
177
|
+
)
|
178
|
+
hits = res[0] if res else []
|
179
|
+
docs: List[Tuple[str, str]] = []
|
180
|
+
for hit in hits:
|
181
|
+
row = hit.entity
|
182
|
+
title = row.get("title") or "Instruction"
|
183
|
+
content = row.get("content") or ""
|
184
|
+
if content:
|
185
|
+
docs.append((title, content))
|
186
|
+
if not docs:
|
187
|
+
try:
|
188
|
+
log_tool_execution(self.agent.name, 'vector_memory.fetch_instructions_tool', 0, success=True)
|
189
|
+
except Exception:
|
190
|
+
pass
|
191
|
+
return ""
|
192
|
+
combined = [f"# {t}\n{c}" for t, c in docs]
|
193
|
+
try:
|
194
|
+
log_tool_execution(self.agent.name, 'vector_memory.fetch_instructions_tool', 0, success=True)
|
195
|
+
except Exception:
|
196
|
+
pass
|
197
|
+
return "\n\n".join(combined)
|
198
|
+
except Exception as e:
|
199
|
+
if self.logger:
|
200
|
+
self.logger.error(f"fetch_instructions_tool error: {e}")
|
201
|
+
try:
|
202
|
+
log_tool_execution(self.agent.name, 'vector_memory.fetch_instructions_tool', 0, success=False)
|
203
|
+
except Exception:
|
204
|
+
pass
|
205
|
+
return ""
|
206
|
+
|
207
|
+
# @tool(description="Owner-only: upload an instruction document to the knowledge base (common or agent-specific).", scope="owner")
|
208
|
+
async def upload_instruction(self, title: str, content: str, agent_specific: bool = True, context: Any = None) -> str:
|
209
|
+
if not self._ensure_collection():
|
210
|
+
return "❌ Vector memory unavailable"
|
211
|
+
try:
|
212
|
+
agent_id = getattr(self.agent, 'id', None) if agent_specific else ""
|
213
|
+
owner_user_id = getattr(self.agent, 'owner_user_id', None) or ""
|
214
|
+
vecs = await self._embed([f"{title}\n\n{content}"])
|
215
|
+
vec = vecs[0] if vecs else [0.0] * self.dim
|
216
|
+
doc_id = uuid.uuid4().hex
|
217
|
+
entities = [
|
218
|
+
[doc_id],
|
219
|
+
[agent_id or ""],
|
220
|
+
[owner_user_id],
|
221
|
+
[title[:255]],
|
222
|
+
[content[:8000]],
|
223
|
+
[vec],
|
224
|
+
]
|
225
|
+
self.collection.insert(entities)
|
226
|
+
try:
|
227
|
+
self.collection.flush()
|
228
|
+
except Exception:
|
229
|
+
pass
|
230
|
+
try:
|
231
|
+
log_tool_execution(self.agent.name, 'vector_memory.upload_instruction', 0, success=True)
|
232
|
+
except Exception:
|
233
|
+
pass
|
234
|
+
return f"✅ Uploaded instruction (id={doc_id})"
|
235
|
+
except Exception as e:
|
236
|
+
if self.logger:
|
237
|
+
self.logger.error(f"upload_instruction error: {e}")
|
238
|
+
try:
|
239
|
+
log_tool_execution(self.agent.name, 'vector_memory.upload_instruction', 0, success=False)
|
240
|
+
except Exception:
|
241
|
+
pass
|
242
|
+
return f"❌ Upload failed: {e}"
|
243
|
+
|
244
|
+
# @tool(description="Owner-only: remove an instruction document by id.", scope="owner")
|
245
|
+
async def remove_instruction(self, doc_id: str, context: Any = None) -> str:
|
246
|
+
if not self._ensure_collection():
|
247
|
+
return "❌ Vector memory unavailable"
|
248
|
+
try:
|
249
|
+
expr = f"id == '{doc_id}'"
|
250
|
+
self.collection.delete(expr)
|
251
|
+
try:
|
252
|
+
self.collection.flush()
|
253
|
+
except Exception:
|
254
|
+
pass
|
255
|
+
try:
|
256
|
+
log_tool_execution(self.agent.name, 'vector_memory.remove_instruction', 0, success=True)
|
257
|
+
except Exception:
|
258
|
+
pass
|
259
|
+
return f"🗑️ Removed instruction (id={doc_id})"
|
260
|
+
except Exception as e:
|
261
|
+
if self.logger:
|
262
|
+
self.logger.error(f"remove_instruction error: {e}")
|
263
|
+
try:
|
264
|
+
log_tool_execution(self.agent.name, 'vector_memory.remove_instruction', 0, success=False)
|
265
|
+
except Exception:
|
266
|
+
pass
|
267
|
+
return f"❌ Remove failed: {e}"
|
268
|
+
|
269
|
+
# ---------------- Admin tools for common instructions ----------------
|
270
|
+
# @tool(description="Admin: list common instruction documents (agent_id == '')", scope="admin")
|
271
|
+
async def list_common_instructions(self, limit: int = 50, context: Any = None) -> str:
|
272
|
+
if not self._ensure_collection():
|
273
|
+
return "❌ Vector memory unavailable"
|
274
|
+
try:
|
275
|
+
# Query all common docs (agent_id == '')
|
276
|
+
# Use a vector-less query via query API
|
277
|
+
results = self.collection.query(
|
278
|
+
expr="agent_id == ''",
|
279
|
+
output_fields=["id", "title", "owner_user_id"],
|
280
|
+
limit=max(1, int(limit))
|
281
|
+
)
|
282
|
+
if not results:
|
283
|
+
try:
|
284
|
+
log_tool_execution(self.agent.name, 'vector_memory.list_common_instructions', 0, success=True)
|
285
|
+
except Exception:
|
286
|
+
pass
|
287
|
+
return "(no common instructions)"
|
288
|
+
lines = [f"- {r.get('id')}: {r.get('title') or 'Untitled'} (owner={r.get('owner_user_id') or ''})" for r in results]
|
289
|
+
try:
|
290
|
+
log_tool_execution(self.agent.name, 'vector_memory.list_common_instructions', 0, success=True)
|
291
|
+
except Exception:
|
292
|
+
pass
|
293
|
+
return "\n".join(lines)
|
294
|
+
except Exception as e:
|
295
|
+
if self.logger:
|
296
|
+
self.logger.error(f"list_common_instructions error: {e}")
|
297
|
+
try:
|
298
|
+
log_tool_execution(self.agent.name, 'vector_memory.list_common_instructions', 0, success=False)
|
299
|
+
except Exception:
|
300
|
+
pass
|
301
|
+
return "❌ List failed"
|
302
|
+
|
303
|
+
# @tool(description="Admin: create a common instruction document", scope="admin")
|
304
|
+
async def create_common_instruction(self, title: str, content: str, context: Any = None) -> str:
|
305
|
+
if not self._ensure_collection():
|
306
|
+
return "❌ Vector memory unavailable"
|
307
|
+
try:
|
308
|
+
vecs = await self._embed([f"{title}\n\n{content}"])
|
309
|
+
vec = vecs[0] if vecs else [0.0] * self.dim
|
310
|
+
doc_id = uuid.uuid4().hex
|
311
|
+
entities = [
|
312
|
+
[doc_id],
|
313
|
+
[""], # agent_id empty => common
|
314
|
+
[getattr(self.agent, 'owner_user_id', '') or ''],
|
315
|
+
[title[:255]],
|
316
|
+
[content[:8000]],
|
317
|
+
[vec],
|
318
|
+
]
|
319
|
+
self.collection.insert(entities)
|
320
|
+
try:
|
321
|
+
self.collection.flush()
|
322
|
+
except Exception:
|
323
|
+
pass
|
324
|
+
try:
|
325
|
+
log_tool_execution(self.agent.name, 'vector_memory.create_common_instruction', 0, success=True)
|
326
|
+
except Exception:
|
327
|
+
pass
|
328
|
+
return f"✅ Created common instruction (id={doc_id})"
|
329
|
+
except Exception as e:
|
330
|
+
if self.logger:
|
331
|
+
self.logger.error(f"create_common_instruction error: {e}")
|
332
|
+
try:
|
333
|
+
log_tool_execution(self.agent.name, 'vector_memory.create_common_instruction', 0, success=False)
|
334
|
+
except Exception:
|
335
|
+
pass
|
336
|
+
return f"❌ Create failed: {e}"
|
337
|
+
|
338
|
+
# @tool(description="Admin: update a common instruction document (title and/or content)", scope="admin")
|
339
|
+
async def update_common_instruction(self, doc_id: str, title: Optional[str] = None, content: Optional[str] = None, context: Any = None) -> str:
|
340
|
+
if not self._ensure_collection():
|
341
|
+
return "❌ Vector memory unavailable"
|
342
|
+
if not title and not content:
|
343
|
+
return "Nothing to update"
|
344
|
+
try:
|
345
|
+
# Fetch existing doc to preserve fields
|
346
|
+
rows = self.collection.query(expr=f"id == '{doc_id}' and agent_id == ''", output_fields=["id", "title", "content"], limit=1)
|
347
|
+
if not rows:
|
348
|
+
return "❌ Document not found or not common"
|
349
|
+
current = rows[0]
|
350
|
+
new_title = title if title is not None else current.get('title')
|
351
|
+
new_content = content if content is not None else current.get('content')
|
352
|
+
vecs = await self._embed([f"{new_title}\n\n{new_content}"])
|
353
|
+
vec = vecs[0] if vecs else [0.0] * self.dim
|
354
|
+
# Delete + insert (Milvus doesn't support partial update of vectors)
|
355
|
+
self.collection.delete(f"id == '{doc_id}'")
|
356
|
+
try:
|
357
|
+
self.collection.flush()
|
358
|
+
except Exception:
|
359
|
+
pass
|
360
|
+
entities = [
|
361
|
+
[doc_id],
|
362
|
+
[""],
|
363
|
+
[getattr(self.agent, 'owner_user_id', '') or ''],
|
364
|
+
[new_title[:255]],
|
365
|
+
[new_content[:8000]],
|
366
|
+
[vec],
|
367
|
+
]
|
368
|
+
self.collection.insert(entities)
|
369
|
+
try:
|
370
|
+
self.collection.flush()
|
371
|
+
except Exception:
|
372
|
+
pass
|
373
|
+
try:
|
374
|
+
log_tool_execution(self.agent.name, 'vector_memory.update_common_instruction', 0, success=True)
|
375
|
+
except Exception:
|
376
|
+
pass
|
377
|
+
return "✅ Updated"
|
378
|
+
except Exception as e:
|
379
|
+
if self.logger:
|
380
|
+
self.logger.error(f"update_common_instruction error: {e}")
|
381
|
+
try:
|
382
|
+
log_tool_execution(self.agent.name, 'vector_memory.update_common_instruction', 0, success=False)
|
383
|
+
except Exception:
|
384
|
+
pass
|
385
|
+
return f"❌ Update failed: {e}"
|
386
|
+
|
387
|
+
# @tool(description="Admin: delete a common instruction document by id", scope="admin")
|
388
|
+
async def delete_common_instruction(self, doc_id: str, context: Any = None) -> str:
|
389
|
+
if not self._ensure_collection():
|
390
|
+
return "❌ Vector memory unavailable"
|
391
|
+
try:
|
392
|
+
self.collection.delete(f"id == '{doc_id}' and agent_id == ''")
|
393
|
+
try:
|
394
|
+
self.collection.flush()
|
395
|
+
except Exception:
|
396
|
+
pass
|
397
|
+
try:
|
398
|
+
log_tool_execution(self.agent.name, 'vector_memory.delete_common_instruction', 0, success=True)
|
399
|
+
except Exception:
|
400
|
+
pass
|
401
|
+
return "🗑️ Deleted"
|
402
|
+
except Exception as e:
|
403
|
+
if self.logger:
|
404
|
+
self.logger.error(f"delete_common_instruction error: {e}")
|
405
|
+
try:
|
406
|
+
log_tool_execution(self.agent.name, 'vector_memory.delete_common_instruction', 0, success=False)
|
407
|
+
except Exception:
|
408
|
+
pass
|
409
|
+
return f"❌ Delete failed: {e}"
|
410
|
+
|
411
|
+
def get_guidance_prompt(self) -> str:
|
412
|
+
return (
|
413
|
+
"You have access to a vector memory of instructions (common and agent-specific).\n"
|
414
|
+
"- You MAY call fetch_instructions_tool ONCE per conversation for domain-specific guidance.\n"
|
415
|
+
"- If the returned instructions are relevant, incorporate them into your reasoning.\n"
|
416
|
+
"- If the returned instructions are irrelevant or generic, ignore them and proceed without them.\n"
|
417
|
+
"- If you are the agent owner or an admin and need to curate knowledge, use the tools\n"
|
418
|
+
" upload_instruction / remove_instruction (owner) or create/update/delete_common_instruction (admin).\n"
|
419
|
+
)
|
420
|
+
|
421
|
+
def get_tool_prompt(self) -> str:
|
422
|
+
"""Detailed prompt block that can be injected into the system prompt."""
|
423
|
+
return (
|
424
|
+
"- fetch_instructions_tool(problem, top_k=1): retrieve relevant instruction documents from memory\n"
|
425
|
+
"Use fetch_instructions_tool ONLY ONCE per conversation and only if you need specific domain guidance.\n"
|
426
|
+
)
|
427
|
+
|
428
|
+
# @prompt blocks to auto-inject scoped guidance
|
429
|
+
@prompt(priority=20, scope="all")
|
430
|
+
def vector_memory_general_prompt(self, context: Any = None) -> str:
|
431
|
+
return self.get_guidance_prompt() + "\n\n" + self.get_tool_prompt()
|
432
|
+
|
433
|
+
@prompt(priority=25, scope="owner")
|
434
|
+
def vector_memory_owner_prompt(self, context: Any = None) -> str:
|
435
|
+
return (
|
436
|
+
"OWNER: You may curate agent-specific instructions using upload_instruction(title, content, agent_specific=True)\n"
|
437
|
+
"and remove_instruction(doc_id). Use these to improve the agent's guidance over time.\n"
|
438
|
+
)
|
439
|
+
|
440
|
+
@prompt(priority=25, scope="admin")
|
441
|
+
def vector_memory_admin_prompt(self, context: Any = None) -> str:
|
442
|
+
return (
|
443
|
+
"ADMIN: You can manage common instructions using list/create/update/delete_common_instruction tools.\n"
|
444
|
+
"Use these to maintain global guidance shared across agents.\n"
|
445
|
+
)
|
446
|
+
|
447
|
+
|