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.
Files changed (112) hide show
  1. rem/agentic/README.md +76 -0
  2. rem/agentic/__init__.py +15 -0
  3. rem/agentic/agents/__init__.py +32 -2
  4. rem/agentic/agents/agent_manager.py +310 -0
  5. rem/agentic/agents/sse_simulator.py +502 -0
  6. rem/agentic/context.py +51 -27
  7. rem/agentic/context_builder.py +5 -3
  8. rem/agentic/llm_provider_models.py +301 -0
  9. rem/agentic/mcp/tool_wrapper.py +155 -18
  10. rem/agentic/otel/setup.py +93 -4
  11. rem/agentic/providers/phoenix.py +371 -108
  12. rem/agentic/providers/pydantic_ai.py +280 -57
  13. rem/agentic/schema.py +361 -21
  14. rem/agentic/tools/rem_tools.py +3 -3
  15. rem/api/README.md +215 -1
  16. rem/api/deps.py +255 -0
  17. rem/api/main.py +132 -40
  18. rem/api/mcp_router/resources.py +1 -1
  19. rem/api/mcp_router/server.py +28 -5
  20. rem/api/mcp_router/tools.py +555 -7
  21. rem/api/routers/admin.py +494 -0
  22. rem/api/routers/auth.py +278 -4
  23. rem/api/routers/chat/completions.py +402 -20
  24. rem/api/routers/chat/models.py +88 -10
  25. rem/api/routers/chat/otel_utils.py +33 -0
  26. rem/api/routers/chat/sse_events.py +542 -0
  27. rem/api/routers/chat/streaming.py +697 -45
  28. rem/api/routers/dev.py +81 -0
  29. rem/api/routers/feedback.py +268 -0
  30. rem/api/routers/messages.py +473 -0
  31. rem/api/routers/models.py +78 -0
  32. rem/api/routers/query.py +360 -0
  33. rem/api/routers/shared_sessions.py +406 -0
  34. rem/auth/__init__.py +13 -3
  35. rem/auth/middleware.py +186 -22
  36. rem/auth/providers/__init__.py +4 -1
  37. rem/auth/providers/email.py +215 -0
  38. rem/cli/commands/README.md +237 -64
  39. rem/cli/commands/cluster.py +1808 -0
  40. rem/cli/commands/configure.py +4 -7
  41. rem/cli/commands/db.py +386 -143
  42. rem/cli/commands/experiments.py +468 -76
  43. rem/cli/commands/process.py +14 -8
  44. rem/cli/commands/schema.py +97 -50
  45. rem/cli/commands/session.py +336 -0
  46. rem/cli/dreaming.py +2 -2
  47. rem/cli/main.py +29 -6
  48. rem/config.py +10 -3
  49. rem/models/core/core_model.py +7 -1
  50. rem/models/core/experiment.py +58 -14
  51. rem/models/core/rem_query.py +5 -2
  52. rem/models/entities/__init__.py +25 -0
  53. rem/models/entities/domain_resource.py +38 -0
  54. rem/models/entities/feedback.py +123 -0
  55. rem/models/entities/message.py +30 -1
  56. rem/models/entities/ontology.py +1 -1
  57. rem/models/entities/ontology_config.py +1 -1
  58. rem/models/entities/session.py +83 -0
  59. rem/models/entities/shared_session.py +180 -0
  60. rem/models/entities/subscriber.py +175 -0
  61. rem/models/entities/user.py +1 -0
  62. rem/registry.py +10 -4
  63. rem/schemas/agents/core/agent-builder.yaml +134 -0
  64. rem/schemas/agents/examples/contract-analyzer.yaml +1 -1
  65. rem/schemas/agents/examples/contract-extractor.yaml +1 -1
  66. rem/schemas/agents/examples/cv-parser.yaml +1 -1
  67. rem/schemas/agents/rem.yaml +7 -3
  68. rem/services/__init__.py +3 -1
  69. rem/services/content/service.py +92 -19
  70. rem/services/email/__init__.py +10 -0
  71. rem/services/email/service.py +459 -0
  72. rem/services/email/templates.py +360 -0
  73. rem/services/embeddings/api.py +4 -4
  74. rem/services/embeddings/worker.py +16 -16
  75. rem/services/phoenix/client.py +154 -14
  76. rem/services/postgres/README.md +197 -15
  77. rem/services/postgres/__init__.py +2 -1
  78. rem/services/postgres/diff_service.py +547 -0
  79. rem/services/postgres/pydantic_to_sqlalchemy.py +470 -140
  80. rem/services/postgres/repository.py +132 -0
  81. rem/services/postgres/schema_generator.py +205 -4
  82. rem/services/postgres/service.py +6 -6
  83. rem/services/rem/parser.py +44 -9
  84. rem/services/rem/service.py +36 -2
  85. rem/services/session/compression.py +137 -51
  86. rem/services/session/reload.py +15 -8
  87. rem/settings.py +515 -27
  88. rem/sql/background_indexes.sql +21 -16
  89. rem/sql/migrations/001_install.sql +387 -54
  90. rem/sql/migrations/002_install_models.sql +2304 -377
  91. rem/sql/migrations/003_optional_extensions.sql +326 -0
  92. rem/sql/migrations/004_cache_system.sql +548 -0
  93. rem/sql/migrations/005_schema_update.sql +145 -0
  94. rem/utils/README.md +45 -0
  95. rem/utils/__init__.py +18 -0
  96. rem/utils/date_utils.py +2 -2
  97. rem/utils/files.py +157 -1
  98. rem/utils/model_helpers.py +156 -1
  99. rem/utils/schema_loader.py +220 -22
  100. rem/utils/sql_paths.py +146 -0
  101. rem/utils/sql_types.py +3 -1
  102. rem/utils/vision.py +1 -1
  103. rem/workers/__init__.py +3 -1
  104. rem/workers/db_listener.py +579 -0
  105. rem/workers/unlogged_maintainer.py +463 -0
  106. {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/METADATA +340 -229
  107. {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/RECORD +109 -80
  108. {remdb-0.3.14.dist-info → remdb-0.3.157.dist-info}/WHEEL +1 -1
  109. rem/sql/002_install_models.sql +0 -1068
  110. rem/sql/install_models.sql +0 -1051
  111. rem/sql/migrations/003_seed_default_user.sql +0 -48
  112. {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
  ]
@@ -1,8 +1,38 @@
1
1
  """
2
2
  REM Agents - Specialized agents for REM operations.
3
3
 
4
- All agents are defined as YAML schemas in src/rem/schemas/agents/.
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
- __all__ = []
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()