remdb 0.2.6__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.
Potentially problematic release.
This version of remdb might be problematic. Click here for more details.
- rem/__init__.py +2 -0
- rem/agentic/README.md +650 -0
- rem/agentic/__init__.py +39 -0
- rem/agentic/agents/README.md +155 -0
- rem/agentic/agents/__init__.py +8 -0
- rem/agentic/context.py +148 -0
- rem/agentic/context_builder.py +329 -0
- rem/agentic/mcp/__init__.py +0 -0
- rem/agentic/mcp/tool_wrapper.py +107 -0
- rem/agentic/otel/__init__.py +5 -0
- rem/agentic/otel/setup.py +151 -0
- rem/agentic/providers/phoenix.py +674 -0
- rem/agentic/providers/pydantic_ai.py +572 -0
- rem/agentic/query.py +117 -0
- rem/agentic/query_helper.py +89 -0
- rem/agentic/schema.py +396 -0
- rem/agentic/serialization.py +245 -0
- rem/agentic/tools/__init__.py +5 -0
- rem/agentic/tools/rem_tools.py +231 -0
- rem/api/README.md +420 -0
- rem/api/main.py +324 -0
- rem/api/mcp_router/prompts.py +182 -0
- rem/api/mcp_router/resources.py +536 -0
- rem/api/mcp_router/server.py +213 -0
- rem/api/mcp_router/tools.py +584 -0
- rem/api/routers/auth.py +229 -0
- rem/api/routers/chat/__init__.py +5 -0
- rem/api/routers/chat/completions.py +281 -0
- rem/api/routers/chat/json_utils.py +76 -0
- rem/api/routers/chat/models.py +124 -0
- rem/api/routers/chat/streaming.py +185 -0
- rem/auth/README.md +258 -0
- rem/auth/__init__.py +26 -0
- rem/auth/middleware.py +100 -0
- rem/auth/providers/__init__.py +13 -0
- rem/auth/providers/base.py +376 -0
- rem/auth/providers/google.py +163 -0
- rem/auth/providers/microsoft.py +237 -0
- rem/cli/README.md +455 -0
- rem/cli/__init__.py +8 -0
- rem/cli/commands/README.md +126 -0
- rem/cli/commands/__init__.py +3 -0
- rem/cli/commands/ask.py +565 -0
- rem/cli/commands/configure.py +423 -0
- rem/cli/commands/db.py +493 -0
- rem/cli/commands/dreaming.py +324 -0
- rem/cli/commands/experiments.py +1124 -0
- rem/cli/commands/mcp.py +66 -0
- rem/cli/commands/process.py +245 -0
- rem/cli/commands/schema.py +183 -0
- rem/cli/commands/serve.py +106 -0
- rem/cli/dreaming.py +363 -0
- rem/cli/main.py +88 -0
- rem/config.py +237 -0
- rem/mcp_server.py +41 -0
- rem/models/core/__init__.py +49 -0
- rem/models/core/core_model.py +64 -0
- rem/models/core/engram.py +333 -0
- rem/models/core/experiment.py +628 -0
- rem/models/core/inline_edge.py +132 -0
- rem/models/core/rem_query.py +243 -0
- rem/models/entities/__init__.py +43 -0
- rem/models/entities/file.py +57 -0
- rem/models/entities/image_resource.py +88 -0
- rem/models/entities/message.py +35 -0
- rem/models/entities/moment.py +123 -0
- rem/models/entities/ontology.py +191 -0
- rem/models/entities/ontology_config.py +131 -0
- rem/models/entities/resource.py +95 -0
- rem/models/entities/schema.py +87 -0
- rem/models/entities/user.py +85 -0
- rem/py.typed +0 -0
- rem/schemas/README.md +507 -0
- rem/schemas/__init__.py +6 -0
- rem/schemas/agents/README.md +92 -0
- rem/schemas/agents/core/moment-builder.yaml +178 -0
- rem/schemas/agents/core/rem-query-agent.yaml +226 -0
- rem/schemas/agents/core/resource-affinity-assessor.yaml +99 -0
- rem/schemas/agents/core/simple-assistant.yaml +19 -0
- rem/schemas/agents/core/user-profile-builder.yaml +163 -0
- rem/schemas/agents/examples/contract-analyzer.yaml +317 -0
- rem/schemas/agents/examples/contract-extractor.yaml +134 -0
- rem/schemas/agents/examples/cv-parser.yaml +263 -0
- rem/schemas/agents/examples/hello-world.yaml +37 -0
- rem/schemas/agents/examples/query.yaml +54 -0
- rem/schemas/agents/examples/simple.yaml +21 -0
- rem/schemas/agents/examples/test.yaml +29 -0
- rem/schemas/agents/rem.yaml +128 -0
- rem/schemas/evaluators/hello-world/default.yaml +77 -0
- rem/schemas/evaluators/rem/faithfulness.yaml +219 -0
- rem/schemas/evaluators/rem/lookup-correctness.yaml +182 -0
- rem/schemas/evaluators/rem/retrieval-precision.yaml +199 -0
- rem/schemas/evaluators/rem/retrieval-recall.yaml +211 -0
- rem/schemas/evaluators/rem/search-correctness.yaml +192 -0
- rem/services/__init__.py +16 -0
- rem/services/audio/INTEGRATION.md +308 -0
- rem/services/audio/README.md +376 -0
- rem/services/audio/__init__.py +15 -0
- rem/services/audio/chunker.py +354 -0
- rem/services/audio/transcriber.py +259 -0
- rem/services/content/README.md +1269 -0
- rem/services/content/__init__.py +5 -0
- rem/services/content/providers.py +806 -0
- rem/services/content/service.py +657 -0
- rem/services/dreaming/README.md +230 -0
- rem/services/dreaming/__init__.py +53 -0
- rem/services/dreaming/affinity_service.py +336 -0
- rem/services/dreaming/moment_service.py +264 -0
- rem/services/dreaming/ontology_service.py +54 -0
- rem/services/dreaming/user_model_service.py +297 -0
- rem/services/dreaming/utils.py +39 -0
- rem/services/embeddings/__init__.py +11 -0
- rem/services/embeddings/api.py +120 -0
- rem/services/embeddings/worker.py +421 -0
- rem/services/fs/README.md +662 -0
- rem/services/fs/__init__.py +62 -0
- rem/services/fs/examples.py +206 -0
- rem/services/fs/examples_paths.py +204 -0
- rem/services/fs/git_provider.py +935 -0
- rem/services/fs/local_provider.py +760 -0
- rem/services/fs/parsing-hooks-examples.md +172 -0
- rem/services/fs/paths.py +276 -0
- rem/services/fs/provider.py +460 -0
- rem/services/fs/s3_provider.py +1042 -0
- rem/services/fs/service.py +186 -0
- rem/services/git/README.md +1075 -0
- rem/services/git/__init__.py +17 -0
- rem/services/git/service.py +469 -0
- rem/services/phoenix/EXPERIMENT_DESIGN.md +1146 -0
- rem/services/phoenix/README.md +453 -0
- rem/services/phoenix/__init__.py +46 -0
- rem/services/phoenix/client.py +686 -0
- rem/services/phoenix/config.py +88 -0
- rem/services/phoenix/prompt_labels.py +477 -0
- rem/services/postgres/README.md +575 -0
- rem/services/postgres/__init__.py +23 -0
- rem/services/postgres/migration_service.py +427 -0
- rem/services/postgres/pydantic_to_sqlalchemy.py +232 -0
- rem/services/postgres/register_type.py +352 -0
- rem/services/postgres/repository.py +337 -0
- rem/services/postgres/schema_generator.py +379 -0
- rem/services/postgres/service.py +802 -0
- rem/services/postgres/sql_builder.py +354 -0
- rem/services/rem/README.md +304 -0
- rem/services/rem/__init__.py +23 -0
- rem/services/rem/exceptions.py +71 -0
- rem/services/rem/executor.py +293 -0
- rem/services/rem/parser.py +145 -0
- rem/services/rem/queries.py +196 -0
- rem/services/rem/query.py +371 -0
- rem/services/rem/service.py +527 -0
- rem/services/session/README.md +374 -0
- rem/services/session/__init__.py +6 -0
- rem/services/session/compression.py +360 -0
- rem/services/session/reload.py +77 -0
- rem/settings.py +1235 -0
- rem/sql/002_install_models.sql +1068 -0
- rem/sql/background_indexes.sql +42 -0
- rem/sql/install_models.sql +1038 -0
- rem/sql/migrations/001_install.sql +503 -0
- rem/sql/migrations/002_install_models.sql +1202 -0
- rem/utils/AGENTIC_CHUNKING.md +597 -0
- rem/utils/README.md +583 -0
- rem/utils/__init__.py +43 -0
- rem/utils/agentic_chunking.py +622 -0
- rem/utils/batch_ops.py +343 -0
- rem/utils/chunking.py +108 -0
- rem/utils/clip_embeddings.py +276 -0
- rem/utils/dict_utils.py +98 -0
- rem/utils/embeddings.py +423 -0
- rem/utils/examples/embeddings_example.py +305 -0
- rem/utils/examples/sql_types_example.py +202 -0
- rem/utils/markdown.py +16 -0
- rem/utils/model_helpers.py +236 -0
- rem/utils/schema_loader.py +229 -0
- rem/utils/sql_types.py +348 -0
- rem/utils/user_id.py +81 -0
- rem/utils/vision.py +330 -0
- rem/workers/README.md +506 -0
- rem/workers/__init__.py +5 -0
- rem/workers/dreaming.py +502 -0
- rem/workers/engram_processor.py +312 -0
- rem/workers/sqs_file_processor.py +193 -0
- remdb-0.2.6.dist-info/METADATA +1191 -0
- remdb-0.2.6.dist-info/RECORD +187 -0
- remdb-0.2.6.dist-info/WHEEL +4 -0
- remdb-0.2.6.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pydantic Serialization Utilities for Agent Results.
|
|
3
|
+
|
|
4
|
+
Critical Pattern:
|
|
5
|
+
When returning Pydantic model instances from agent results (especially in MCP tools,
|
|
6
|
+
API responses, or any serialization context), ALWAYS serialize them explicitly using
|
|
7
|
+
.model_dump() or .model_dump_json() before returning.
|
|
8
|
+
|
|
9
|
+
Why This Matters:
|
|
10
|
+
- FastMCP, FastAPI, and other frameworks may use their own serialization logic
|
|
11
|
+
- Pydantic models returned directly may not include all fields during serialization
|
|
12
|
+
- Newly added fields might be silently dropped if not explicitly serialized
|
|
13
|
+
- result.output or result.data may be a Pydantic model instance, not a dict
|
|
14
|
+
|
|
15
|
+
Common Anti-Patterns to Avoid:
|
|
16
|
+
```python
|
|
17
|
+
# ❌ BAD: Returns Pydantic model directly
|
|
18
|
+
return {
|
|
19
|
+
"status": "success",
|
|
20
|
+
"response": result.output, # Pydantic model instance!
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# ✅ GOOD: Explicitly serialize first
|
|
24
|
+
return {
|
|
25
|
+
"status": "success",
|
|
26
|
+
"response": result.output.model_dump(), # Serialized dict
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Design Rules:
|
|
31
|
+
1. Always check if object has .model_dump() or .model_dump_json()
|
|
32
|
+
2. Use serialize_agent_result() for consistent handling
|
|
33
|
+
3. In streaming contexts, use .model_dump_json() for SSE
|
|
34
|
+
4. Document when functions return Pydantic models vs dicts
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from typing import Any, cast
|
|
38
|
+
|
|
39
|
+
from pydantic import BaseModel
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def serialize_agent_result(result: Any) -> dict[str, Any] | str:
|
|
43
|
+
"""
|
|
44
|
+
Safely serialize an agent result, handling Pydantic models correctly.
|
|
45
|
+
|
|
46
|
+
This function ensures that Pydantic model instances are properly serialized
|
|
47
|
+
before being returned from API endpoints, MCP tools, or any other context
|
|
48
|
+
where serialization is critical.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
result: Agent result which may be:
|
|
52
|
+
- Pydantic model instance (has .model_dump())
|
|
53
|
+
- Dict (already serialized)
|
|
54
|
+
- Primitive type (str, int, bool, None)
|
|
55
|
+
- List or other collection
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Serialized result as dict or primitive type
|
|
59
|
+
|
|
60
|
+
Examples:
|
|
61
|
+
>>> # With Pydantic model result
|
|
62
|
+
>>> agent_result = await agent.run(query)
|
|
63
|
+
>>> serialized = serialize_agent_result(agent_result.output)
|
|
64
|
+
>>> return {"response": serialized} # Safe to serialize
|
|
65
|
+
|
|
66
|
+
>>> # With already-serialized result
|
|
67
|
+
>>> data = {"key": "value"}
|
|
68
|
+
>>> serialized = serialize_agent_result(data)
|
|
69
|
+
>>> assert serialized == data # No-op for dicts
|
|
70
|
+
|
|
71
|
+
>>> # With primitive result
|
|
72
|
+
>>> result = "Hello world"
|
|
73
|
+
>>> serialized = serialize_agent_result(result)
|
|
74
|
+
>>> assert serialized == result # No-op for primitives
|
|
75
|
+
"""
|
|
76
|
+
# Check if this is a Pydantic model instance
|
|
77
|
+
if isinstance(result, BaseModel):
|
|
78
|
+
return result.model_dump()
|
|
79
|
+
|
|
80
|
+
# Check if this has a model_dump method (duck typing)
|
|
81
|
+
if hasattr(result, "model_dump") and callable(getattr(result, "model_dump")):
|
|
82
|
+
return cast(dict[str, Any] | str, result.model_dump())
|
|
83
|
+
|
|
84
|
+
# Already a dict or primitive - return as-is
|
|
85
|
+
return cast(dict[str, Any] | str, result)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def serialize_agent_result_json(result: Any) -> str:
|
|
89
|
+
"""
|
|
90
|
+
Safely serialize an agent result to JSON string, handling Pydantic models correctly.
|
|
91
|
+
|
|
92
|
+
Use this variant when you need a JSON string output (e.g., for SSE streaming,
|
|
93
|
+
JSON responses, or storage).
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
result: Agent result which may be:
|
|
97
|
+
- Pydantic model instance (has .model_dump_json())
|
|
98
|
+
- Dict or other JSON-serializable object
|
|
99
|
+
- Primitive type
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
JSON string representation
|
|
103
|
+
|
|
104
|
+
Examples:
|
|
105
|
+
>>> # With Pydantic model result
|
|
106
|
+
>>> agent_result = await agent.run(query)
|
|
107
|
+
>>> json_str = serialize_agent_result_json(agent_result.output)
|
|
108
|
+
>>> return Response(content=json_str, media_type="application/json")
|
|
109
|
+
|
|
110
|
+
>>> # For SSE streaming
|
|
111
|
+
>>> chunk = serialize_agent_result_json(result.output)
|
|
112
|
+
>>> yield f"data: {chunk}\\n\\n"
|
|
113
|
+
"""
|
|
114
|
+
import json
|
|
115
|
+
|
|
116
|
+
# Check if this is a Pydantic model instance with model_dump_json
|
|
117
|
+
if isinstance(result, BaseModel):
|
|
118
|
+
return result.model_dump_json()
|
|
119
|
+
|
|
120
|
+
# Check if this has a model_dump_json method (duck typing)
|
|
121
|
+
if hasattr(result, "model_dump_json") and callable(
|
|
122
|
+
getattr(result, "model_dump_json")
|
|
123
|
+
):
|
|
124
|
+
return cast(str, result.model_dump_json())
|
|
125
|
+
|
|
126
|
+
# Fall back to standard json.dumps
|
|
127
|
+
return json.dumps(result)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def is_pydantic_model(obj: Any) -> bool:
|
|
131
|
+
"""
|
|
132
|
+
Check if an object is a Pydantic model instance.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
obj: Object to check
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
True if object is a Pydantic model instance
|
|
139
|
+
|
|
140
|
+
Examples:
|
|
141
|
+
>>> from pydantic import BaseModel
|
|
142
|
+
>>> class MyModel(BaseModel):
|
|
143
|
+
... value: str
|
|
144
|
+
>>> instance = MyModel(value="test")
|
|
145
|
+
>>> assert is_pydantic_model(instance) == True
|
|
146
|
+
>>> assert is_pydantic_model({"value": "test"}) == False
|
|
147
|
+
"""
|
|
148
|
+
return isinstance(obj, BaseModel) or (
|
|
149
|
+
hasattr(obj, "model_dump") and hasattr(obj, "model_fields")
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def safe_serialize_dict(data: dict[str, Any]) -> dict[str, Any]:
|
|
154
|
+
"""
|
|
155
|
+
Recursively serialize a dict that may contain Pydantic models.
|
|
156
|
+
|
|
157
|
+
Use this when you have a dict that may contain Pydantic model instances
|
|
158
|
+
nested within it (e.g., as values).
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
data: Dict that may contain Pydantic models
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Dict with all Pydantic models serialized to dicts
|
|
165
|
+
|
|
166
|
+
Examples:
|
|
167
|
+
>>> # Dict with nested Pydantic model
|
|
168
|
+
>>> data = {
|
|
169
|
+
... "status": "success",
|
|
170
|
+
... "result": some_pydantic_model, # Will be serialized
|
|
171
|
+
... "metadata": {"count": 5}
|
|
172
|
+
... }
|
|
173
|
+
>>> serialized = safe_serialize_dict(data)
|
|
174
|
+
>>> # All Pydantic models are now dicts
|
|
175
|
+
"""
|
|
176
|
+
result = {}
|
|
177
|
+
for key, value in data.items():
|
|
178
|
+
if is_pydantic_model(value):
|
|
179
|
+
result[key] = serialize_agent_result(value)
|
|
180
|
+
elif isinstance(value, dict):
|
|
181
|
+
result[key] = safe_serialize_dict(value)
|
|
182
|
+
elif isinstance(value, list):
|
|
183
|
+
result[key] = [
|
|
184
|
+
serialize_agent_result(item) if is_pydantic_model(item) else item
|
|
185
|
+
for item in value
|
|
186
|
+
]
|
|
187
|
+
else:
|
|
188
|
+
result[key] = value
|
|
189
|
+
return result
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# Example usage patterns for documentation
|
|
193
|
+
USAGE_EXAMPLES = """
|
|
194
|
+
# Example 1: MCP Tool returning agent result
|
|
195
|
+
async def ask_rem_tool(query: str) -> dict[str, Any]:
|
|
196
|
+
from rem.agentic.serialization import serialize_agent_result
|
|
197
|
+
|
|
198
|
+
agent = await create_agent()
|
|
199
|
+
result = await agent.run(query)
|
|
200
|
+
|
|
201
|
+
# ✅ GOOD: Serialize before returning
|
|
202
|
+
return {
|
|
203
|
+
"status": "success",
|
|
204
|
+
"response": serialize_agent_result(result.output),
|
|
205
|
+
"model": result.model,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# Example 2: API endpoint with Pydantic result
|
|
209
|
+
@app.post("/query")
|
|
210
|
+
async def query_endpoint(body: QueryRequest):
|
|
211
|
+
from rem.agentic.serialization import serialize_agent_result
|
|
212
|
+
|
|
213
|
+
agent = await create_agent()
|
|
214
|
+
result = await agent.run(body.query)
|
|
215
|
+
|
|
216
|
+
# ✅ GOOD: Serialize for FastAPI response
|
|
217
|
+
return {
|
|
218
|
+
"data": serialize_agent_result(result.output),
|
|
219
|
+
"usage": result.usage().model_dump() if result.usage() else None,
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# Example 3: Streaming with SSE
|
|
223
|
+
async def stream_results(agent, query):
|
|
224
|
+
from rem.agentic.serialization import serialize_agent_result_json
|
|
225
|
+
|
|
226
|
+
async with agent.iter(query) as run:
|
|
227
|
+
async for event in run:
|
|
228
|
+
if isinstance(event, SomeEvent):
|
|
229
|
+
# ✅ GOOD: Serialize to JSON string for SSE
|
|
230
|
+
json_str = serialize_agent_result_json(event.data)
|
|
231
|
+
yield f"data: {json_str}\\n\\n"
|
|
232
|
+
|
|
233
|
+
# Example 4: Service layer returning to MCP tool
|
|
234
|
+
async def ask_rem(query: str, tenant_id: str) -> dict[str, Any]:
|
|
235
|
+
from rem.agentic.serialization import serialize_agent_result
|
|
236
|
+
|
|
237
|
+
agent = await create_agent()
|
|
238
|
+
result = await agent.run(query)
|
|
239
|
+
|
|
240
|
+
# ✅ GOOD: Serialize in service layer
|
|
241
|
+
return {
|
|
242
|
+
"query_output": serialize_agent_result(result.data),
|
|
243
|
+
"natural_query": query,
|
|
244
|
+
}
|
|
245
|
+
"""
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""
|
|
2
|
+
REM tools for agent execution (CLI and API compatible).
|
|
3
|
+
|
|
4
|
+
These tools work in both CLI and API contexts by initializing services on-demand.
|
|
5
|
+
They wrap the service layer directly, not MCP tools.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Literal, cast
|
|
9
|
+
|
|
10
|
+
from loguru import logger
|
|
11
|
+
|
|
12
|
+
from ...models.core import (
|
|
13
|
+
FuzzyParameters,
|
|
14
|
+
LookupParameters,
|
|
15
|
+
QueryType,
|
|
16
|
+
RemQuery,
|
|
17
|
+
SearchParameters,
|
|
18
|
+
SQLParameters,
|
|
19
|
+
TraverseParameters,
|
|
20
|
+
)
|
|
21
|
+
from ...services.content import ContentService
|
|
22
|
+
from ...services.postgres import get_postgres_service
|
|
23
|
+
from ...services.rem import RemService
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Service cache for reuse within agent execution
|
|
27
|
+
_service_cache: dict[str, Any] = {}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def _get_rem_service() -> RemService:
|
|
31
|
+
"""Get or create RemService instance."""
|
|
32
|
+
if "rem_service" not in _service_cache:
|
|
33
|
+
db = get_postgres_service()
|
|
34
|
+
if not db:
|
|
35
|
+
raise RuntimeError("PostgreSQL is disabled. Cannot use REM service.")
|
|
36
|
+
|
|
37
|
+
await db.connect()
|
|
38
|
+
_service_cache["postgres"] = db
|
|
39
|
+
_service_cache["rem_service"] = RemService(postgres_service=db)
|
|
40
|
+
logger.debug("Initialized RemService for agent tools")
|
|
41
|
+
return cast(RemService, _service_cache["rem_service"])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def search_rem_tool(
|
|
45
|
+
query_type: Literal["lookup", "fuzzy", "search", "sql", "traverse"],
|
|
46
|
+
user_id: str,
|
|
47
|
+
# LOOKUP parameters
|
|
48
|
+
entity_key: str | None = None,
|
|
49
|
+
# FUZZY parameters
|
|
50
|
+
query_text: str | None = None,
|
|
51
|
+
threshold: float = 0.7,
|
|
52
|
+
# SEARCH parameters
|
|
53
|
+
table: str | None = None,
|
|
54
|
+
limit: int = 20,
|
|
55
|
+
# SQL parameters
|
|
56
|
+
sql_query: str | None = None,
|
|
57
|
+
# TRAVERSE parameters
|
|
58
|
+
initial_query: str | None = None,
|
|
59
|
+
edge_types: list[str] | None = None,
|
|
60
|
+
depth: int = 1,
|
|
61
|
+
) -> dict[str, Any]:
|
|
62
|
+
"""
|
|
63
|
+
Execute REM queries for entity lookup, semantic search, and graph traversal.
|
|
64
|
+
|
|
65
|
+
This tool works in both CLI and API contexts by initializing services on-demand.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
query_type: Type of query (lookup, fuzzy, search, sql, traverse)
|
|
69
|
+
user_id: User identifier for data scoping
|
|
70
|
+
entity_key: Entity key for LOOKUP (e.g., "Sarah Chen")
|
|
71
|
+
query_text: Search text for FUZZY or SEARCH
|
|
72
|
+
threshold: Similarity threshold for FUZZY (0.0-1.0)
|
|
73
|
+
table: Target table for SEARCH (resources, moments, users, etc.)
|
|
74
|
+
limit: Max results for SEARCH
|
|
75
|
+
sql_query: SQL query string for SQL type
|
|
76
|
+
initial_query: Starting entity for TRAVERSE
|
|
77
|
+
edge_types: Edge types to follow for TRAVERSE
|
|
78
|
+
depth: Traversal depth for TRAVERSE
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Dict with query results and metadata
|
|
82
|
+
"""
|
|
83
|
+
try:
|
|
84
|
+
rem_service = await _get_rem_service()
|
|
85
|
+
|
|
86
|
+
# Build RemQuery based on query_type
|
|
87
|
+
if query_type == "lookup":
|
|
88
|
+
if not entity_key:
|
|
89
|
+
return {"status": "error", "error": "entity_key required for LOOKUP"}
|
|
90
|
+
|
|
91
|
+
query = RemQuery(
|
|
92
|
+
query_type=QueryType.LOOKUP,
|
|
93
|
+
parameters=LookupParameters(
|
|
94
|
+
key=entity_key,
|
|
95
|
+
user_id=user_id,
|
|
96
|
+
),
|
|
97
|
+
user_id=user_id,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
elif query_type == "fuzzy":
|
|
101
|
+
if not query_text:
|
|
102
|
+
return {"status": "error", "error": "query_text required for FUZZY"}
|
|
103
|
+
|
|
104
|
+
query = RemQuery(
|
|
105
|
+
query_type=QueryType.FUZZY,
|
|
106
|
+
parameters=FuzzyParameters(
|
|
107
|
+
query_text=query_text,
|
|
108
|
+
threshold=threshold,
|
|
109
|
+
limit=limit, # Implied parameter
|
|
110
|
+
),
|
|
111
|
+
user_id=user_id,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
elif query_type == "search":
|
|
115
|
+
if not query_text:
|
|
116
|
+
return {"status": "error", "error": "query_text required for SEARCH"}
|
|
117
|
+
if not table:
|
|
118
|
+
return {"status": "error", "error": "table required for SEARCH"}
|
|
119
|
+
|
|
120
|
+
query = RemQuery(
|
|
121
|
+
query_type=QueryType.SEARCH,
|
|
122
|
+
parameters=SearchParameters(
|
|
123
|
+
query_text=query_text,
|
|
124
|
+
table_name=table,
|
|
125
|
+
limit=limit,
|
|
126
|
+
),
|
|
127
|
+
user_id=user_id,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
elif query_type == "sql":
|
|
131
|
+
if not sql_query:
|
|
132
|
+
return {"status": "error", "error": "sql_query required for SQL"}
|
|
133
|
+
|
|
134
|
+
if not table:
|
|
135
|
+
return {"status": "error", "error": "table required for SQL queries"}
|
|
136
|
+
|
|
137
|
+
query = RemQuery(
|
|
138
|
+
query_type=QueryType.SQL,
|
|
139
|
+
parameters=SQLParameters(
|
|
140
|
+
table_name=table,
|
|
141
|
+
where_clause=sql_query,
|
|
142
|
+
limit=limit, # SQLParams accepts limit
|
|
143
|
+
),
|
|
144
|
+
user_id=user_id,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
elif query_type == "traverse":
|
|
148
|
+
if not initial_query:
|
|
149
|
+
return {"status": "error", "error": "initial_query required for TRAVERSE"}
|
|
150
|
+
|
|
151
|
+
query = RemQuery(
|
|
152
|
+
query_type=QueryType.TRAVERSE,
|
|
153
|
+
parameters=TraverseParameters(
|
|
154
|
+
initial_query=initial_query,
|
|
155
|
+
edge_types=edge_types or [],
|
|
156
|
+
max_depth=depth,
|
|
157
|
+
),
|
|
158
|
+
user_id=user_id,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
else:
|
|
162
|
+
return {"status": "error", "error": f"Unknown query_type: {query_type}"}
|
|
163
|
+
|
|
164
|
+
# Execute query
|
|
165
|
+
logger.info(f"Executing REM query: {query_type} for user {user_id}")
|
|
166
|
+
result = await rem_service.execute_query(query)
|
|
167
|
+
|
|
168
|
+
logger.info(f"Query completed: {query_type}")
|
|
169
|
+
return {
|
|
170
|
+
"status": "success",
|
|
171
|
+
"query_type": query_type,
|
|
172
|
+
"results": result,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
except Exception as e:
|
|
176
|
+
logger.error(f"search_rem_tool failed: {e}", exc_info=True)
|
|
177
|
+
return {
|
|
178
|
+
"status": "error",
|
|
179
|
+
"error": str(e),
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
async def ingest_file_tool(
|
|
184
|
+
file_uri: str,
|
|
185
|
+
user_id: str,
|
|
186
|
+
category: str | None = None,
|
|
187
|
+
tags: list[str] | None = None,
|
|
188
|
+
is_local_server: bool = True, # CLI is always local
|
|
189
|
+
) -> dict[str, Any]:
|
|
190
|
+
"""
|
|
191
|
+
Ingest file into REM (read + store + parse + chunk + embed).
|
|
192
|
+
|
|
193
|
+
This tool works in both CLI and API contexts.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
file_uri: File location (local path, s3:// URI, or http(s):// URL)
|
|
197
|
+
user_id: User identifier for data scoping
|
|
198
|
+
category: Optional category (document, code, audio, etc.)
|
|
199
|
+
tags: Optional tags for file
|
|
200
|
+
is_local_server: True if running as local CLI (default)
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Dict with file_id, processing_status, resources_created, etc.
|
|
204
|
+
"""
|
|
205
|
+
try:
|
|
206
|
+
content_service = ContentService()
|
|
207
|
+
result = await content_service.ingest_file(
|
|
208
|
+
file_uri=file_uri,
|
|
209
|
+
user_id=user_id,
|
|
210
|
+
category=category,
|
|
211
|
+
tags=tags,
|
|
212
|
+
is_local_server=is_local_server,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
logger.info(
|
|
216
|
+
f"File ingestion complete: {result['file_name']} "
|
|
217
|
+
f"(status: {result['processing_status']}, "
|
|
218
|
+
f"resources: {result['resources_created']})"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
"status": "success",
|
|
223
|
+
**result,
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
except Exception as e:
|
|
227
|
+
logger.error(f"ingest_file_tool failed: {e}", exc_info=True)
|
|
228
|
+
return {
|
|
229
|
+
"status": "error",
|
|
230
|
+
"error": str(e),
|
|
231
|
+
}
|