remdb 0.3.14__py3-none-any.whl → 0.3.157__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.
- rem/agentic/README.md +76 -0
- rem/agentic/__init__.py +15 -0
- rem/agentic/agents/__init__.py +32 -2
- rem/agentic/agents/agent_manager.py +310 -0
- rem/agentic/agents/sse_simulator.py +502 -0
- rem/agentic/context.py +51 -27
- rem/agentic/context_builder.py +5 -3
- rem/agentic/llm_provider_models.py +301 -0
- rem/agentic/mcp/tool_wrapper.py +155 -18
- rem/agentic/otel/setup.py +93 -4
- rem/agentic/providers/phoenix.py +371 -108
- rem/agentic/providers/pydantic_ai.py +280 -57
- rem/agentic/schema.py +361 -21
- rem/agentic/tools/rem_tools.py +3 -3
- rem/api/README.md +215 -1
- rem/api/deps.py +255 -0
- rem/api/main.py +132 -40
- rem/api/mcp_router/resources.py +1 -1
- rem/api/mcp_router/server.py +28 -5
- rem/api/mcp_router/tools.py +555 -7
- rem/api/routers/admin.py +494 -0
- rem/api/routers/auth.py +278 -4
- rem/api/routers/chat/completions.py +402 -20
- rem/api/routers/chat/models.py +88 -10
- rem/api/routers/chat/otel_utils.py +33 -0
- rem/api/routers/chat/sse_events.py +542 -0
- rem/api/routers/chat/streaming.py +697 -45
- rem/api/routers/dev.py +81 -0
- rem/api/routers/feedback.py +268 -0
- rem/api/routers/messages.py +473 -0
- rem/api/routers/models.py +78 -0
- rem/api/routers/query.py +360 -0
- rem/api/routers/shared_sessions.py +406 -0
- rem/auth/__init__.py +13 -3
- rem/auth/middleware.py +186 -22
- rem/auth/providers/__init__.py +4 -1
- rem/auth/providers/email.py +215 -0
- rem/cli/commands/README.md +237 -64
- rem/cli/commands/cluster.py +1808 -0
- rem/cli/commands/configure.py +4 -7
- rem/cli/commands/db.py +386 -143
- rem/cli/commands/experiments.py +468 -76
- rem/cli/commands/process.py +14 -8
- rem/cli/commands/schema.py +97 -50
- rem/cli/commands/session.py +336 -0
- rem/cli/dreaming.py +2 -2
- rem/cli/main.py +29 -6
- rem/config.py +10 -3
- rem/models/core/core_model.py +7 -1
- rem/models/core/experiment.py +58 -14
- rem/models/core/rem_query.py +5 -2
- rem/models/entities/__init__.py +25 -0
- rem/models/entities/domain_resource.py +38 -0
- rem/models/entities/feedback.py +123 -0
- rem/models/entities/message.py +30 -1
- rem/models/entities/ontology.py +1 -1
- rem/models/entities/ontology_config.py +1 -1
- rem/models/entities/session.py +83 -0
- rem/models/entities/shared_session.py +180 -0
- rem/models/entities/subscriber.py +175 -0
- rem/models/entities/user.py +1 -0
- rem/registry.py +10 -4
- rem/schemas/agents/core/agent-builder.yaml +134 -0
- rem/schemas/agents/examples/contract-analyzer.yaml +1 -1
- rem/schemas/agents/examples/contract-extractor.yaml +1 -1
- rem/schemas/agents/examples/cv-parser.yaml +1 -1
- rem/schemas/agents/rem.yaml +7 -3
- rem/services/__init__.py +3 -1
- rem/services/content/service.py +92 -19
- rem/services/email/__init__.py +10 -0
- rem/services/email/service.py +459 -0
- rem/services/email/templates.py +360 -0
- rem/services/embeddings/api.py +4 -4
- rem/services/embeddings/worker.py +16 -16
- rem/services/phoenix/client.py +154 -14
- rem/services/postgres/README.md +197 -15
- rem/services/postgres/__init__.py +2 -1
- rem/services/postgres/diff_service.py +547 -0
- rem/services/postgres/pydantic_to_sqlalchemy.py +470 -140
- rem/services/postgres/repository.py +132 -0
- rem/services/postgres/schema_generator.py +205 -4
- rem/services/postgres/service.py +6 -6
- rem/services/rem/parser.py +44 -9
- rem/services/rem/service.py +36 -2
- rem/services/session/compression.py +137 -51
- rem/services/session/reload.py +15 -8
- rem/settings.py +515 -27
- rem/sql/background_indexes.sql +21 -16
- rem/sql/migrations/001_install.sql +387 -54
- rem/sql/migrations/002_install_models.sql +2304 -377
- rem/sql/migrations/003_optional_extensions.sql +326 -0
- rem/sql/migrations/004_cache_system.sql +548 -0
- rem/sql/migrations/005_schema_update.sql +145 -0
- rem/utils/README.md +45 -0
- rem/utils/__init__.py +18 -0
- rem/utils/date_utils.py +2 -2
- rem/utils/files.py +157 -1
- rem/utils/model_helpers.py +156 -1
- rem/utils/schema_loader.py +220 -22
- rem/utils/sql_paths.py +146 -0
- rem/utils/sql_types.py +3 -1
- rem/utils/vision.py +1 -1
- rem/workers/__init__.py +3 -1
- rem/workers/db_listener.py +579 -0
- rem/workers/unlogged_maintainer.py +463 -0
- {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/METADATA +340 -229
- {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/RECORD +109 -80
- {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/WHEEL +1 -1
- rem/sql/002_install_models.sql +0 -1068
- rem/sql/install_models.sql +0 -1051
- rem/sql/migrations/003_seed_default_user.sql +0 -48
- {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/entry_points.txt +0 -0
rem/agentic/README.md
CHANGED
|
@@ -640,6 +640,82 @@ If `query_agent_model` is not set, the agent uses `settings.llm.default_model`.
|
|
|
640
640
|
- PostgreSQL dialect aware (knows about KV_STORE, embeddings tables)
|
|
641
641
|
- Can generate multi-step query plans for complex questions
|
|
642
642
|
|
|
643
|
+
## SSE Simulator
|
|
644
|
+
|
|
645
|
+
The SSE Simulator is a **programmatic** event generator (not an LLM-based agent) that produces
|
|
646
|
+
a scripted sequence of SSE events for testing and demonstrating the streaming protocol.
|
|
647
|
+
|
|
648
|
+
### Purpose
|
|
649
|
+
|
|
650
|
+
- Frontend development without LLM costs
|
|
651
|
+
- Testing SSE parsing and rendering
|
|
652
|
+
- Demonstrating the full event protocol
|
|
653
|
+
- Load testing streaming infrastructure
|
|
654
|
+
|
|
655
|
+
### Usage
|
|
656
|
+
|
|
657
|
+
```python
|
|
658
|
+
from rem.agentic.agents import stream_simulator_events
|
|
659
|
+
from rem.api.routers.chat.sse_events import format_sse_event
|
|
660
|
+
|
|
661
|
+
# Generate all event types
|
|
662
|
+
async for event in stream_simulator_events("demo"):
|
|
663
|
+
print(format_sse_event(event))
|
|
664
|
+
|
|
665
|
+
# Minimal demo (text + done only)
|
|
666
|
+
from rem.agentic.agents import stream_minimal_demo
|
|
667
|
+
async for event in stream_minimal_demo("Hello world!"):
|
|
668
|
+
print(event)
|
|
669
|
+
|
|
670
|
+
# Error simulation
|
|
671
|
+
from rem.agentic.agents import stream_error_demo
|
|
672
|
+
async for event in stream_error_demo(error_after_words=10):
|
|
673
|
+
print(event)
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
### Event Sequence
|
|
677
|
+
|
|
678
|
+
The full simulator produces events in this order:
|
|
679
|
+
|
|
680
|
+
1. **Reasoning** (4 steps) - Model thinking process
|
|
681
|
+
2. **Progress** (step 1/4) - Starting
|
|
682
|
+
3. **Tool calls** (2 tools) - Simulated tool invocations
|
|
683
|
+
4. **Progress** (step 2/4) - Generating
|
|
684
|
+
5. **Text deltas** - Streamed markdown content
|
|
685
|
+
6. **Progress** (step 3/4) - Formatting
|
|
686
|
+
7. **Metadata** - Confidence, sources, flags
|
|
687
|
+
8. **Progress** (step 4/4) - Preparing actions
|
|
688
|
+
9. **Action request** - Feedback card with buttons and inputs
|
|
689
|
+
10. **Progress** (all complete)
|
|
690
|
+
11. **Done** - Stream completion
|
|
691
|
+
|
|
692
|
+
### Configuration Options
|
|
693
|
+
|
|
694
|
+
```python
|
|
695
|
+
await stream_simulator_events(
|
|
696
|
+
prompt="demo",
|
|
697
|
+
delay_ms=50, # Delay between events
|
|
698
|
+
include_reasoning=True, # Emit reasoning events
|
|
699
|
+
include_progress=True, # Emit progress events
|
|
700
|
+
include_tool_calls=True, # Emit tool call events
|
|
701
|
+
include_actions=True, # Emit action request at end
|
|
702
|
+
include_metadata=True, # Emit metadata event
|
|
703
|
+
)
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### HTTP Endpoint
|
|
707
|
+
|
|
708
|
+
Use the simulator via the chat completions endpoint:
|
|
709
|
+
|
|
710
|
+
```bash
|
|
711
|
+
curl -X POST http://localhost:8000/api/v1/chat/completions \
|
|
712
|
+
-H "Content-Type: application/json" \
|
|
713
|
+
-H "X-Agent-Schema: simulator" \
|
|
714
|
+
-d '{"messages": [{"role": "user", "content": "demo"}], "stream": true}'
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
See `rem/api/README.md` for full SSE event protocol documentation.
|
|
718
|
+
|
|
643
719
|
## Future Work
|
|
644
720
|
|
|
645
721
|
- [ ] Phoenix evaluator integration
|
rem/agentic/__init__.py
CHANGED
|
@@ -17,6 +17,14 @@ from .schema import (
|
|
|
17
17
|
)
|
|
18
18
|
from .providers.pydantic_ai import create_agent_from_schema_file, create_agent, AgentRuntime
|
|
19
19
|
from .query_helper import ask_rem, REMQueryOutput
|
|
20
|
+
from .llm_provider_models import (
|
|
21
|
+
ModelInfo,
|
|
22
|
+
AVAILABLE_MODELS,
|
|
23
|
+
ALLOWED_MODEL_IDS,
|
|
24
|
+
is_valid_model,
|
|
25
|
+
get_valid_model_or_default,
|
|
26
|
+
get_model_by_id,
|
|
27
|
+
)
|
|
20
28
|
|
|
21
29
|
__all__ = [
|
|
22
30
|
# Context and Query
|
|
@@ -36,4 +44,11 @@ __all__ = [
|
|
|
36
44
|
# REM Query Helpers
|
|
37
45
|
"ask_rem",
|
|
38
46
|
"REMQueryOutput",
|
|
47
|
+
# LLM Provider Models
|
|
48
|
+
"ModelInfo",
|
|
49
|
+
"AVAILABLE_MODELS",
|
|
50
|
+
"ALLOWED_MODEL_IDS",
|
|
51
|
+
"is_valid_model",
|
|
52
|
+
"get_valid_model_or_default",
|
|
53
|
+
"get_model_by_id",
|
|
39
54
|
]
|
rem/agentic/agents/__init__.py
CHANGED
|
@@ -1,8 +1,38 @@
|
|
|
1
1
|
"""
|
|
2
2
|
REM Agents - Specialized agents for REM operations.
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Most agents are defined as YAML schemas in src/rem/schemas/agents/.
|
|
5
5
|
Use create_agent_from_schema_file() to instantiate agents.
|
|
6
|
+
|
|
7
|
+
The SSE Simulator is a special programmatic "agent" that generates
|
|
8
|
+
scripted SSE events for testing and demonstration - it doesn't use an LLM.
|
|
9
|
+
|
|
10
|
+
Agent Manager provides functions for saving/loading user-created agents.
|
|
6
11
|
"""
|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
from .sse_simulator import (
|
|
14
|
+
stream_simulator_events,
|
|
15
|
+
stream_minimal_demo,
|
|
16
|
+
stream_error_demo,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from .agent_manager import (
|
|
20
|
+
save_agent,
|
|
21
|
+
get_agent,
|
|
22
|
+
list_agents,
|
|
23
|
+
delete_agent,
|
|
24
|
+
build_agent_spec,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
# SSE Simulator (programmatic, no LLM)
|
|
29
|
+
"stream_simulator_events",
|
|
30
|
+
"stream_minimal_demo",
|
|
31
|
+
"stream_error_demo",
|
|
32
|
+
# Agent Manager
|
|
33
|
+
"save_agent",
|
|
34
|
+
"get_agent",
|
|
35
|
+
"list_agents",
|
|
36
|
+
"delete_agent",
|
|
37
|
+
"build_agent_spec",
|
|
38
|
+
]
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent Manager - Save, load, and manage user-created agents.
|
|
3
|
+
|
|
4
|
+
This module provides the core functionality for persisting agent schemas
|
|
5
|
+
to the database with user scoping.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from rem.agentic.agents.agent_manager import save_agent, get_agent, list_agents
|
|
9
|
+
|
|
10
|
+
# Save an agent
|
|
11
|
+
result = await save_agent(
|
|
12
|
+
name="my-assistant",
|
|
13
|
+
description="You are a helpful assistant.",
|
|
14
|
+
user_id="user-123"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# Get an agent
|
|
18
|
+
agent = await get_agent("my-assistant", user_id="user-123")
|
|
19
|
+
|
|
20
|
+
# List user's agents
|
|
21
|
+
agents = await list_agents(user_id="user-123")
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from typing import Any
|
|
25
|
+
from loguru import logger
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
DEFAULT_TOOLS = ["search_rem", "register_metadata"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def build_agent_spec(
|
|
32
|
+
name: str,
|
|
33
|
+
description: str,
|
|
34
|
+
properties: dict[str, Any] | None = None,
|
|
35
|
+
required: list[str] | None = None,
|
|
36
|
+
tools: list[str] | None = None,
|
|
37
|
+
tags: list[str] | None = None,
|
|
38
|
+
version: str = "1.0.0",
|
|
39
|
+
) -> dict[str, Any]:
|
|
40
|
+
"""
|
|
41
|
+
Build a valid agent schema spec.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
name: Agent name in kebab-case
|
|
45
|
+
description: System prompt for the agent
|
|
46
|
+
properties: Output schema properties
|
|
47
|
+
required: Required property names
|
|
48
|
+
tools: Tool names (defaults to search_rem, register_metadata)
|
|
49
|
+
tags: Categorization tags
|
|
50
|
+
version: Semantic version
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Valid agent schema spec dict
|
|
54
|
+
"""
|
|
55
|
+
# Default properties
|
|
56
|
+
if properties is None:
|
|
57
|
+
properties = {
|
|
58
|
+
"answer": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"description": "Natural language response to the user"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Default required
|
|
65
|
+
if required is None:
|
|
66
|
+
required = ["answer"]
|
|
67
|
+
|
|
68
|
+
# Default tools
|
|
69
|
+
if tools is None:
|
|
70
|
+
tools = DEFAULT_TOOLS.copy()
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"description": description,
|
|
75
|
+
"properties": properties,
|
|
76
|
+
"required": required,
|
|
77
|
+
"json_schema_extra": {
|
|
78
|
+
"kind": "agent",
|
|
79
|
+
"name": name,
|
|
80
|
+
"version": version,
|
|
81
|
+
"tags": tags or [],
|
|
82
|
+
"tools": [{"name": t, "description": f"Tool: {t}"} for t in tools],
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async def save_agent(
|
|
88
|
+
name: str,
|
|
89
|
+
description: str,
|
|
90
|
+
user_id: str,
|
|
91
|
+
properties: dict[str, Any] | None = None,
|
|
92
|
+
required: list[str] | None = None,
|
|
93
|
+
tools: list[str] | None = None,
|
|
94
|
+
tags: list[str] | None = None,
|
|
95
|
+
version: str = "1.0.0",
|
|
96
|
+
) -> dict[str, Any]:
|
|
97
|
+
"""
|
|
98
|
+
Save an agent schema to the database.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
name: Agent name in kebab-case (e.g., "code-reviewer")
|
|
102
|
+
description: The agent's system prompt
|
|
103
|
+
user_id: User identifier for scoping
|
|
104
|
+
properties: Output schema properties
|
|
105
|
+
required: Required property names
|
|
106
|
+
tools: Tool names
|
|
107
|
+
tags: Categorization tags
|
|
108
|
+
version: Semantic version
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Dict with status, agent_name, version, message
|
|
112
|
+
|
|
113
|
+
Raises:
|
|
114
|
+
RuntimeError: If database is not available
|
|
115
|
+
"""
|
|
116
|
+
from rem.models.entities import Schema
|
|
117
|
+
from rem.services.postgres import get_postgres_service
|
|
118
|
+
|
|
119
|
+
# Build the spec
|
|
120
|
+
spec = build_agent_spec(
|
|
121
|
+
name=name,
|
|
122
|
+
description=description,
|
|
123
|
+
properties=properties,
|
|
124
|
+
required=required,
|
|
125
|
+
tools=tools,
|
|
126
|
+
tags=tags,
|
|
127
|
+
version=version,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Create Schema entity (user-scoped)
|
|
131
|
+
schema_entity = Schema(
|
|
132
|
+
tenant_id=user_id,
|
|
133
|
+
user_id=user_id,
|
|
134
|
+
name=name,
|
|
135
|
+
spec=spec,
|
|
136
|
+
category="agent",
|
|
137
|
+
metadata={
|
|
138
|
+
"version": version,
|
|
139
|
+
"tags": tags or [],
|
|
140
|
+
"created_via": "agent_manager",
|
|
141
|
+
},
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Save to database
|
|
145
|
+
postgres = get_postgres_service()
|
|
146
|
+
if not postgres:
|
|
147
|
+
raise RuntimeError("Database not available")
|
|
148
|
+
|
|
149
|
+
await postgres.connect()
|
|
150
|
+
try:
|
|
151
|
+
await postgres.batch_upsert(
|
|
152
|
+
records=[schema_entity],
|
|
153
|
+
model=Schema,
|
|
154
|
+
table_name="schemas",
|
|
155
|
+
entity_key_field="name",
|
|
156
|
+
generate_embeddings=False,
|
|
157
|
+
)
|
|
158
|
+
logger.info(f"✅ Agent saved: {name} (user={user_id}, version={version})")
|
|
159
|
+
finally:
|
|
160
|
+
await postgres.disconnect()
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
"status": "success",
|
|
164
|
+
"agent_name": name,
|
|
165
|
+
"version": version,
|
|
166
|
+
"message": f"Agent '{name}' saved successfully.",
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
async def get_agent(
|
|
171
|
+
name: str,
|
|
172
|
+
user_id: str,
|
|
173
|
+
) -> dict[str, Any] | None:
|
|
174
|
+
"""
|
|
175
|
+
Get an agent schema by name.
|
|
176
|
+
|
|
177
|
+
Checks user's schemas first, then falls back to system schemas.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
name: Agent name
|
|
181
|
+
user_id: User identifier
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Agent spec dict if found, None otherwise
|
|
185
|
+
"""
|
|
186
|
+
from rem.services.postgres import get_postgres_service
|
|
187
|
+
|
|
188
|
+
postgres = get_postgres_service()
|
|
189
|
+
if not postgres:
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
await postgres.connect()
|
|
193
|
+
try:
|
|
194
|
+
query = """
|
|
195
|
+
SELECT spec FROM schemas
|
|
196
|
+
WHERE LOWER(name) = LOWER($1)
|
|
197
|
+
AND category = 'agent'
|
|
198
|
+
AND (user_id = $2 OR user_id IS NULL OR tenant_id = 'system')
|
|
199
|
+
ORDER BY CASE WHEN user_id = $2 THEN 0 ELSE 1 END
|
|
200
|
+
LIMIT 1
|
|
201
|
+
"""
|
|
202
|
+
row = await postgres.fetchrow(query, name, user_id)
|
|
203
|
+
if row:
|
|
204
|
+
return row["spec"]
|
|
205
|
+
return None
|
|
206
|
+
finally:
|
|
207
|
+
await postgres.disconnect()
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
async def list_agents(
|
|
211
|
+
user_id: str,
|
|
212
|
+
include_system: bool = True,
|
|
213
|
+
) -> list[dict[str, Any]]:
|
|
214
|
+
"""
|
|
215
|
+
List available agents for a user.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
user_id: User identifier
|
|
219
|
+
include_system: Include system agents
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
List of agent metadata dicts
|
|
223
|
+
"""
|
|
224
|
+
from rem.services.postgres import get_postgres_service
|
|
225
|
+
|
|
226
|
+
postgres = get_postgres_service()
|
|
227
|
+
if not postgres:
|
|
228
|
+
return []
|
|
229
|
+
|
|
230
|
+
await postgres.connect()
|
|
231
|
+
try:
|
|
232
|
+
if include_system:
|
|
233
|
+
query = """
|
|
234
|
+
SELECT name, metadata, user_id, tenant_id
|
|
235
|
+
FROM schemas
|
|
236
|
+
WHERE category = 'agent'
|
|
237
|
+
AND (user_id = $1 OR user_id IS NULL OR tenant_id = 'system')
|
|
238
|
+
ORDER BY name
|
|
239
|
+
"""
|
|
240
|
+
rows = await postgres.fetch(query, user_id)
|
|
241
|
+
else:
|
|
242
|
+
query = """
|
|
243
|
+
SELECT name, metadata, user_id, tenant_id
|
|
244
|
+
FROM schemas
|
|
245
|
+
WHERE category = 'agent'
|
|
246
|
+
AND user_id = $1
|
|
247
|
+
ORDER BY name
|
|
248
|
+
"""
|
|
249
|
+
rows = await postgres.fetch(query, user_id)
|
|
250
|
+
|
|
251
|
+
return [
|
|
252
|
+
{
|
|
253
|
+
"name": row["name"],
|
|
254
|
+
"version": row["metadata"].get("version", "1.0.0") if row["metadata"] else "1.0.0",
|
|
255
|
+
"tags": row["metadata"].get("tags", []) if row["metadata"] else [],
|
|
256
|
+
"is_system": row["tenant_id"] == "system" or row["user_id"] is None,
|
|
257
|
+
}
|
|
258
|
+
for row in rows
|
|
259
|
+
]
|
|
260
|
+
finally:
|
|
261
|
+
await postgres.disconnect()
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
async def delete_agent(
|
|
265
|
+
name: str,
|
|
266
|
+
user_id: str,
|
|
267
|
+
) -> dict[str, Any]:
|
|
268
|
+
"""
|
|
269
|
+
Delete a user's agent.
|
|
270
|
+
|
|
271
|
+
Only allows deleting user-owned agents, not system agents.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
name: Agent name
|
|
275
|
+
user_id: User identifier
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
Dict with status and message
|
|
279
|
+
"""
|
|
280
|
+
from rem.services.postgres import get_postgres_service
|
|
281
|
+
|
|
282
|
+
postgres = get_postgres_service()
|
|
283
|
+
if not postgres:
|
|
284
|
+
raise RuntimeError("Database not available")
|
|
285
|
+
|
|
286
|
+
await postgres.connect()
|
|
287
|
+
try:
|
|
288
|
+
# Only delete user's own agents
|
|
289
|
+
query = """
|
|
290
|
+
DELETE FROM schemas
|
|
291
|
+
WHERE LOWER(name) = LOWER($1)
|
|
292
|
+
AND category = 'agent'
|
|
293
|
+
AND user_id = $2
|
|
294
|
+
RETURNING name
|
|
295
|
+
"""
|
|
296
|
+
row = await postgres.fetchrow(query, name, user_id)
|
|
297
|
+
|
|
298
|
+
if row:
|
|
299
|
+
logger.info(f"🗑️ Agent deleted: {name} (user={user_id})")
|
|
300
|
+
return {
|
|
301
|
+
"status": "success",
|
|
302
|
+
"message": f"Agent '{name}' deleted.",
|
|
303
|
+
}
|
|
304
|
+
else:
|
|
305
|
+
return {
|
|
306
|
+
"status": "error",
|
|
307
|
+
"message": f"Agent '{name}' not found or not owned by you.",
|
|
308
|
+
}
|
|
309
|
+
finally:
|
|
310
|
+
await postgres.disconnect()
|