gobby 0.2.5__py3-none-any.whl → 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.
- gobby/adapters/claude_code.py +13 -4
- gobby/adapters/codex.py +43 -3
- gobby/agents/runner.py +8 -0
- gobby/cli/__init__.py +6 -0
- gobby/cli/clones.py +419 -0
- gobby/cli/conductor.py +266 -0
- gobby/cli/installers/antigravity.py +3 -9
- gobby/cli/installers/claude.py +9 -9
- gobby/cli/installers/codex.py +2 -8
- gobby/cli/installers/gemini.py +2 -8
- gobby/cli/installers/shared.py +71 -8
- gobby/cli/skills.py +858 -0
- gobby/cli/tasks/ai.py +0 -440
- gobby/cli/tasks/crud.py +44 -6
- gobby/cli/tasks/main.py +0 -4
- gobby/cli/tui.py +2 -2
- gobby/cli/utils.py +3 -3
- gobby/clones/__init__.py +13 -0
- gobby/clones/git.py +547 -0
- gobby/conductor/__init__.py +16 -0
- gobby/conductor/alerts.py +135 -0
- gobby/conductor/loop.py +164 -0
- gobby/conductor/monitors/__init__.py +11 -0
- gobby/conductor/monitors/agents.py +116 -0
- gobby/conductor/monitors/tasks.py +155 -0
- gobby/conductor/pricing.py +234 -0
- gobby/conductor/token_tracker.py +160 -0
- gobby/config/app.py +63 -1
- gobby/config/search.py +110 -0
- gobby/config/servers.py +1 -1
- gobby/config/skills.py +43 -0
- gobby/config/tasks.py +6 -14
- gobby/hooks/event_handlers.py +145 -2
- gobby/hooks/hook_manager.py +48 -2
- gobby/hooks/skill_manager.py +130 -0
- gobby/install/claude/hooks/hook_dispatcher.py +4 -4
- gobby/install/codex/hooks/hook_dispatcher.py +1 -1
- gobby/install/gemini/hooks/hook_dispatcher.py +87 -12
- gobby/llm/claude.py +22 -34
- gobby/llm/claude_executor.py +46 -256
- gobby/llm/codex_executor.py +59 -291
- gobby/llm/executor.py +21 -0
- gobby/llm/gemini.py +134 -110
- gobby/llm/litellm_executor.py +143 -6
- gobby/llm/resolver.py +95 -33
- gobby/mcp_proxy/instructions.py +54 -0
- gobby/mcp_proxy/models.py +15 -0
- gobby/mcp_proxy/registries.py +68 -5
- gobby/mcp_proxy/server.py +33 -3
- gobby/mcp_proxy/services/tool_proxy.py +81 -1
- gobby/mcp_proxy/stdio.py +2 -1
- gobby/mcp_proxy/tools/__init__.py +0 -2
- gobby/mcp_proxy/tools/agent_messaging.py +317 -0
- gobby/mcp_proxy/tools/clones.py +903 -0
- gobby/mcp_proxy/tools/memory.py +1 -24
- gobby/mcp_proxy/tools/metrics.py +65 -1
- gobby/mcp_proxy/tools/orchestration/__init__.py +3 -0
- gobby/mcp_proxy/tools/orchestration/cleanup.py +151 -0
- gobby/mcp_proxy/tools/orchestration/wait.py +467 -0
- gobby/mcp_proxy/tools/session_messages.py +1 -2
- gobby/mcp_proxy/tools/skills/__init__.py +631 -0
- gobby/mcp_proxy/tools/task_orchestration.py +7 -0
- gobby/mcp_proxy/tools/task_readiness.py +14 -0
- gobby/mcp_proxy/tools/task_sync.py +1 -1
- gobby/mcp_proxy/tools/tasks/_context.py +0 -20
- gobby/mcp_proxy/tools/tasks/_crud.py +91 -4
- gobby/mcp_proxy/tools/tasks/_expansion.py +348 -0
- gobby/mcp_proxy/tools/tasks/_factory.py +6 -16
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +60 -29
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +18 -29
- gobby/mcp_proxy/tools/workflows.py +1 -1
- gobby/mcp_proxy/tools/worktrees.py +5 -0
- gobby/memory/backends/__init__.py +6 -1
- gobby/memory/backends/mem0.py +6 -1
- gobby/memory/extractor.py +477 -0
- gobby/memory/manager.py +11 -2
- gobby/prompts/defaults/handoff/compact.md +63 -0
- gobby/prompts/defaults/handoff/session_end.md +57 -0
- gobby/prompts/defaults/memory/extract.md +61 -0
- gobby/runner.py +37 -16
- gobby/search/__init__.py +48 -6
- gobby/search/backends/__init__.py +159 -0
- gobby/search/backends/embedding.py +225 -0
- gobby/search/embeddings.py +238 -0
- gobby/search/models.py +148 -0
- gobby/search/unified.py +496 -0
- gobby/servers/http.py +23 -8
- gobby/servers/routes/admin.py +280 -0
- gobby/servers/routes/mcp/tools.py +241 -52
- gobby/servers/websocket.py +2 -2
- gobby/sessions/analyzer.py +2 -0
- gobby/sessions/transcripts/base.py +1 -0
- gobby/sessions/transcripts/claude.py +64 -5
- gobby/skills/__init__.py +91 -0
- gobby/skills/loader.py +685 -0
- gobby/skills/manager.py +384 -0
- gobby/skills/parser.py +258 -0
- gobby/skills/search.py +463 -0
- gobby/skills/sync.py +119 -0
- gobby/skills/updater.py +385 -0
- gobby/skills/validator.py +368 -0
- gobby/storage/clones.py +378 -0
- gobby/storage/database.py +1 -1
- gobby/storage/memories.py +43 -13
- gobby/storage/migrations.py +180 -6
- gobby/storage/sessions.py +73 -0
- gobby/storage/skills.py +749 -0
- gobby/storage/tasks/_crud.py +4 -4
- gobby/storage/tasks/_lifecycle.py +41 -6
- gobby/storage/tasks/_manager.py +14 -5
- gobby/storage/tasks/_models.py +8 -3
- gobby/sync/memories.py +39 -4
- gobby/sync/tasks.py +83 -6
- gobby/tasks/__init__.py +1 -2
- gobby/tasks/validation.py +24 -15
- gobby/tui/api_client.py +4 -7
- gobby/tui/app.py +5 -3
- gobby/tui/screens/orchestrator.py +1 -2
- gobby/tui/screens/tasks.py +2 -4
- gobby/tui/ws_client.py +1 -1
- gobby/utils/daemon_client.py +2 -2
- gobby/workflows/actions.py +84 -2
- gobby/workflows/context_actions.py +43 -0
- gobby/workflows/detection_helpers.py +115 -31
- gobby/workflows/engine.py +13 -2
- gobby/workflows/lifecycle_evaluator.py +29 -1
- gobby/workflows/loader.py +19 -6
- gobby/workflows/memory_actions.py +74 -0
- gobby/workflows/summary_actions.py +17 -0
- gobby/workflows/task_enforcement_actions.py +448 -6
- {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/METADATA +82 -21
- {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/RECORD +136 -107
- gobby/install/codex/prompts/forget.md +0 -7
- gobby/install/codex/prompts/memories.md +0 -7
- gobby/install/codex/prompts/recall.md +0 -7
- gobby/install/codex/prompts/remember.md +0 -13
- gobby/llm/gemini_executor.py +0 -339
- gobby/mcp_proxy/tools/task_expansion.py +0 -591
- gobby/tasks/context.py +0 -747
- gobby/tasks/criteria.py +0 -342
- gobby/tasks/expansion.py +0 -626
- gobby/tasks/prompts/expand.py +0 -327
- gobby/tasks/research.py +0 -421
- gobby/tasks/tdd.py +0 -352
- {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/WHEEL +0 -0
- {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/entry_points.txt +0 -0
- {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/licenses/LICENSE.md +0 -0
- {gobby-0.2.5.dist-info → gobby-0.2.6.dist-info}/top_level.txt +0 -0
|
@@ -41,7 +41,6 @@ def create_lifecycle_registry(ctx: RegistryContext) -> InternalToolRegistry:
|
|
|
41
41
|
skip_validation: bool = False,
|
|
42
42
|
session_id: str | None = None,
|
|
43
43
|
override_justification: str | None = None,
|
|
44
|
-
no_commit_needed: bool = False,
|
|
45
44
|
commit_sha: str | None = None,
|
|
46
45
|
) -> dict[str, Any]:
|
|
47
46
|
"""Close a task with validation.
|
|
@@ -51,14 +50,12 @@ def create_lifecycle_registry(ctx: RegistryContext) -> InternalToolRegistry:
|
|
|
51
50
|
|
|
52
51
|
Args:
|
|
53
52
|
task_id: Task reference (#N, path, or UUID)
|
|
54
|
-
reason: Reason for closing
|
|
53
|
+
reason: Reason for closing. Use "duplicate", "already_implemented", "wont_fix",
|
|
54
|
+
or "obsolete" to auto-skip commit check (these imply no work was done).
|
|
55
55
|
changes_summary: Summary of changes (enables LLM validation for leaf tasks)
|
|
56
56
|
skip_validation: Skip all validation checks
|
|
57
57
|
session_id: Session ID where task is being closed (auto-links to session)
|
|
58
58
|
override_justification: Why agent bypassed validation (stored for audit).
|
|
59
|
-
Also used to explain why no commit was needed when no_commit_needed=True.
|
|
60
|
-
no_commit_needed: Set to True for tasks that don't produce code changes
|
|
61
|
-
(research, planning, documentation review). Requires override_justification.
|
|
62
59
|
commit_sha: Git commit SHA to link before closing. Convenience for link + close in one call.
|
|
63
60
|
|
|
64
61
|
Returns:
|
|
@@ -85,9 +82,7 @@ def create_lifecycle_registry(ctx: RegistryContext) -> InternalToolRegistry:
|
|
|
85
82
|
cwd = repo_path or "."
|
|
86
83
|
|
|
87
84
|
# Check for linked commits (unless task type doesn't require commits)
|
|
88
|
-
commit_result = validate_commit_requirements(
|
|
89
|
-
task, reason, no_commit_needed, override_justification
|
|
90
|
-
)
|
|
85
|
+
commit_result = validate_commit_requirements(task, reason, repo_path)
|
|
91
86
|
if not commit_result.can_close:
|
|
92
87
|
return {
|
|
93
88
|
"error": commit_result.error_type,
|
|
@@ -141,7 +136,7 @@ def create_lifecycle_registry(ctx: RegistryContext) -> InternalToolRegistry:
|
|
|
141
136
|
|
|
142
137
|
# Determine close outcome
|
|
143
138
|
route_to_review, store_override = determine_close_outcome(
|
|
144
|
-
task, skip_validation,
|
|
139
|
+
task, skip_validation, override_justification
|
|
145
140
|
)
|
|
146
141
|
|
|
147
142
|
# Get git commit SHA (best-effort, dynamic short format for consistency)
|
|
@@ -194,6 +189,23 @@ def create_lifecycle_registry(ctx: RegistryContext) -> InternalToolRegistry:
|
|
|
194
189
|
except Exception:
|
|
195
190
|
pass # nosec B110 - best-effort linking, don't fail the close
|
|
196
191
|
|
|
192
|
+
# Clear workflow task_claimed state if this was the claimed task
|
|
193
|
+
# Respects the clear_task_on_close variable (defaults to True if not set)
|
|
194
|
+
# This is done here because Claude Code's post-tool-use hook doesn't include
|
|
195
|
+
# the tool result, so the detection_helpers can't verify close succeeded
|
|
196
|
+
if session_id:
|
|
197
|
+
try:
|
|
198
|
+
state = ctx.workflow_state_manager.get_state(session_id)
|
|
199
|
+
if state and state.variables.get("claimed_task_id") == resolved_id:
|
|
200
|
+
# Check if clear_task_on_close is enabled (default: True)
|
|
201
|
+
clear_on_close = state.variables.get("clear_task_on_close", True)
|
|
202
|
+
if clear_on_close:
|
|
203
|
+
state.variables["task_claimed"] = False
|
|
204
|
+
state.variables["claimed_task_id"] = None
|
|
205
|
+
ctx.workflow_state_manager.save_state(state)
|
|
206
|
+
except Exception:
|
|
207
|
+
pass # nosec B110 - best-effort state update
|
|
208
|
+
|
|
197
209
|
# Update worktree status based on closure reason (case-insensitive)
|
|
198
210
|
try:
|
|
199
211
|
reason_normalized = reason.lower()
|
|
@@ -216,7 +228,7 @@ def create_lifecycle_registry(ctx: RegistryContext) -> InternalToolRegistry:
|
|
|
216
228
|
|
|
217
229
|
registry.register(
|
|
218
230
|
name="close_task",
|
|
219
|
-
description="Close a task.
|
|
231
|
+
description="Close a task. Pass commit_sha to link and close in one call: close_task(task_id, commit_sha='abc123'). Or include [#N] in commit message for auto-linking. Parent tasks require all children closed. Validation auto-skipped for: duplicate, already_implemented, wont_fix, obsolete.",
|
|
220
232
|
input_schema={
|
|
221
233
|
"type": "object",
|
|
222
234
|
"properties": {
|
|
@@ -251,24 +263,14 @@ def create_lifecycle_registry(ctx: RegistryContext) -> InternalToolRegistry:
|
|
|
251
263
|
"override_justification": {
|
|
252
264
|
"type": "string",
|
|
253
265
|
"description": (
|
|
254
|
-
"Justification for bypassing validation
|
|
255
|
-
"Required when skip_validation=True or no_commit_needed=True. "
|
|
266
|
+
"Justification for bypassing validation. Required when skip_validation=True. "
|
|
256
267
|
"Example: 'Validation saw truncated diff - verified via git show that commit includes all changes'"
|
|
257
268
|
),
|
|
258
269
|
"default": None,
|
|
259
270
|
},
|
|
260
|
-
"no_commit_needed": {
|
|
261
|
-
"type": "boolean",
|
|
262
|
-
"description": (
|
|
263
|
-
"ONLY for tasks with NO code changes (pure research, planning, documentation review). "
|
|
264
|
-
"Do NOT use this to bypass validation when a commit exists - use skip_validation instead. "
|
|
265
|
-
"Requires override_justification."
|
|
266
|
-
),
|
|
267
|
-
"default": False,
|
|
268
|
-
},
|
|
269
271
|
"commit_sha": {
|
|
270
272
|
"type": "string",
|
|
271
|
-
"description": "Git commit SHA to link
|
|
273
|
+
"description": "RECOMMENDED: Git commit SHA to link and close in one call. Use this instead of separate link_commit + close_task calls.",
|
|
272
274
|
"default": None,
|
|
273
275
|
},
|
|
274
276
|
},
|
|
@@ -334,8 +336,12 @@ def create_lifecycle_registry(ctx: RegistryContext) -> InternalToolRegistry:
|
|
|
334
336
|
func=reopen_task,
|
|
335
337
|
)
|
|
336
338
|
|
|
337
|
-
def delete_task(task_id: str, cascade: bool = True) -> dict[str, Any]:
|
|
338
|
-
"""Delete a task
|
|
339
|
+
def delete_task(task_id: str, cascade: bool = True, unlink: bool = False) -> dict[str, Any]:
|
|
340
|
+
"""Delete a task.
|
|
341
|
+
|
|
342
|
+
By default (cascade=True), deletes subtasks and dependent tasks.
|
|
343
|
+
Use unlink=True to remove dependency links but preserve dependent tasks.
|
|
344
|
+
"""
|
|
339
345
|
try:
|
|
340
346
|
resolved_id = resolve_task_id_for_mcp(ctx.task_manager, task_id)
|
|
341
347
|
except (TaskNotFoundError, ValueError) as e:
|
|
@@ -347,9 +353,26 @@ def create_lifecycle_registry(ctx: RegistryContext) -> InternalToolRegistry:
|
|
|
347
353
|
return {"error": f"Task {task_id} not found"}
|
|
348
354
|
ref = f"#{task.seq_num}" if task.seq_num else resolved_id[:8]
|
|
349
355
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
356
|
+
try:
|
|
357
|
+
deleted = ctx.task_manager.delete_task(resolved_id, cascade=cascade, unlink=unlink)
|
|
358
|
+
if not deleted:
|
|
359
|
+
return {"error": f"Task {task_id} not found"}
|
|
360
|
+
except ValueError as e:
|
|
361
|
+
error_msg = str(e)
|
|
362
|
+
if "dependent task(s)" in error_msg:
|
|
363
|
+
return {
|
|
364
|
+
"error": "has_dependents",
|
|
365
|
+
"message": error_msg,
|
|
366
|
+
"suggestion": f"Use cascade=True to delete task {ref} and its dependents, "
|
|
367
|
+
f"or unlink=True to preserve dependent tasks.",
|
|
368
|
+
}
|
|
369
|
+
elif "has children" in error_msg:
|
|
370
|
+
return {
|
|
371
|
+
"error": "has_children",
|
|
372
|
+
"message": error_msg,
|
|
373
|
+
"suggestion": f"Use cascade=True to delete task {ref} and all its subtasks.",
|
|
374
|
+
}
|
|
375
|
+
return {"error": error_msg}
|
|
353
376
|
|
|
354
377
|
return {
|
|
355
378
|
"ref": ref,
|
|
@@ -358,7 +381,9 @@ def create_lifecycle_registry(ctx: RegistryContext) -> InternalToolRegistry:
|
|
|
358
381
|
|
|
359
382
|
registry.register(
|
|
360
383
|
name="delete_task",
|
|
361
|
-
description="Delete a task and
|
|
384
|
+
description="Delete a task. By default (cascade=True), deletes subtasks and dependent tasks. "
|
|
385
|
+
"Set cascade=False to fail if task has children or dependents. "
|
|
386
|
+
"Use unlink=True to remove dependency links but preserve dependent tasks.",
|
|
362
387
|
input_schema={
|
|
363
388
|
"type": "object",
|
|
364
389
|
"properties": {
|
|
@@ -368,9 +393,15 @@ def create_lifecycle_registry(ctx: RegistryContext) -> InternalToolRegistry:
|
|
|
368
393
|
},
|
|
369
394
|
"cascade": {
|
|
370
395
|
"type": "boolean",
|
|
371
|
-
"description": "If True, delete
|
|
396
|
+
"description": "If True, delete subtasks and dependent tasks. Defaults to True.",
|
|
372
397
|
"default": True,
|
|
373
398
|
},
|
|
399
|
+
"unlink": {
|
|
400
|
+
"type": "boolean",
|
|
401
|
+
"description": "If True, remove dependency links but preserve dependent tasks. "
|
|
402
|
+
"Ignored if cascade=True.",
|
|
403
|
+
"default": False,
|
|
404
|
+
},
|
|
374
405
|
},
|
|
375
406
|
"required": ["task_id"],
|
|
376
407
|
},
|
|
@@ -33,16 +33,14 @@ class ValidationResult:
|
|
|
33
33
|
def validate_commit_requirements(
|
|
34
34
|
task: Task,
|
|
35
35
|
reason: str,
|
|
36
|
-
|
|
37
|
-
override_justification: str | None,
|
|
36
|
+
repo_path: str | None = None,
|
|
38
37
|
) -> ValidationResult:
|
|
39
38
|
"""Check if task meets commit requirements for closing.
|
|
40
39
|
|
|
41
40
|
Args:
|
|
42
41
|
task: The task to validate
|
|
43
42
|
reason: Reason for closing
|
|
44
|
-
|
|
45
|
-
override_justification: Justification for skipping commit check
|
|
43
|
+
repo_path: Path to the repository for git operations
|
|
46
44
|
|
|
47
45
|
Returns:
|
|
48
46
|
ValidationResult indicating if task can be closed
|
|
@@ -51,28 +49,21 @@ def validate_commit_requirements(
|
|
|
51
49
|
requires_commit_check = reason.lower() not in SKIP_REASONS
|
|
52
50
|
|
|
53
51
|
if requires_commit_check and not task.commits:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
error_type="no_commits_linked",
|
|
70
|
-
message=(
|
|
71
|
-
"Cannot close task: no commits are linked. Either:\n"
|
|
72
|
-
"1. Commit your changes and use link_commit() or include [task_id] in commit message\n"
|
|
73
|
-
"2. Set no_commit_needed=True with override_justification if this task didn't require code changes"
|
|
74
|
-
),
|
|
75
|
-
)
|
|
52
|
+
return ValidationResult(
|
|
53
|
+
can_close=False,
|
|
54
|
+
error_type="no_commits_linked",
|
|
55
|
+
message=(
|
|
56
|
+
"A commit is required before closing this task.\n\n"
|
|
57
|
+
"**Normal flow:**\n"
|
|
58
|
+
'1. Commit your changes: git commit -m "[#N] description"\n'
|
|
59
|
+
'2. Close with commit_sha: close_task(task_id="#N", commit_sha="<sha>")\n\n'
|
|
60
|
+
"**Edge cases (no work done):**\n"
|
|
61
|
+
'- Task was already done: reason="already_implemented"\n'
|
|
62
|
+
'- Task is no longer needed: reason="obsolete"\n'
|
|
63
|
+
'- Task duplicates another: reason="duplicate"\n'
|
|
64
|
+
'- Decided not to do it: reason="wont_fix"'
|
|
65
|
+
),
|
|
66
|
+
)
|
|
76
67
|
|
|
77
68
|
return ValidationResult(can_close=True)
|
|
78
69
|
|
|
@@ -277,7 +268,6 @@ async def validate_leaf_task_with_llm(
|
|
|
277
268
|
def determine_close_outcome(
|
|
278
269
|
task: Task,
|
|
279
270
|
skip_validation: bool,
|
|
280
|
-
no_commit_needed: bool,
|
|
281
271
|
override_justification: str | None,
|
|
282
272
|
) -> tuple[bool, bool]:
|
|
283
273
|
"""Determine the close outcome for a task.
|
|
@@ -285,14 +275,13 @@ def determine_close_outcome(
|
|
|
285
275
|
Args:
|
|
286
276
|
task: The task being closed
|
|
287
277
|
skip_validation: Whether validation was skipped
|
|
288
|
-
no_commit_needed: Whether commit was not needed
|
|
289
278
|
override_justification: Justification for override
|
|
290
279
|
|
|
291
280
|
Returns:
|
|
292
281
|
Tuple of (route_to_review, store_override)
|
|
293
282
|
"""
|
|
294
283
|
# Determine if override should be stored
|
|
295
|
-
store_override = skip_validation
|
|
284
|
+
store_override = skip_validation
|
|
296
285
|
|
|
297
286
|
# Route to review if task requires user review OR override was used
|
|
298
287
|
# This ensures tasks with HITL flag or skipped validation go through human review
|
|
@@ -1014,6 +1014,11 @@ def create_worktrees_registry(
|
|
|
1014
1014
|
if isinstance(terminal, str):
|
|
1015
1015
|
terminal = terminal.lower()
|
|
1016
1016
|
|
|
1017
|
+
# Default to 'worktree-agent' workflow if not specified
|
|
1018
|
+
# This workflow restricts tools available to spawned agents in worktrees
|
|
1019
|
+
if workflow is None:
|
|
1020
|
+
workflow = "worktree-agent"
|
|
1021
|
+
|
|
1017
1022
|
# Validate workflow (reject lifecycle workflows)
|
|
1018
1023
|
if workflow:
|
|
1019
1024
|
workflow_loader = WorkflowLoader()
|
|
@@ -76,7 +76,12 @@ def get_backend(backend_type: str, **kwargs: Any) -> MemoryBackendProtocol:
|
|
|
76
76
|
return NullBackend()
|
|
77
77
|
|
|
78
78
|
elif backend_type == "mem0":
|
|
79
|
-
|
|
79
|
+
try:
|
|
80
|
+
from gobby.memory.backends.mem0 import Mem0Backend
|
|
81
|
+
except ImportError as e:
|
|
82
|
+
raise ImportError(
|
|
83
|
+
"mem0ai is not installed. Install with: pip install gobby[mem0]"
|
|
84
|
+
) from e
|
|
80
85
|
|
|
81
86
|
api_key: str | None = kwargs.get("api_key")
|
|
82
87
|
if api_key is None:
|
gobby/memory/backends/mem0.py
CHANGED
|
@@ -60,7 +60,12 @@ class Mem0Backend:
|
|
|
60
60
|
**kwargs: Additional MemoryClient configuration
|
|
61
61
|
"""
|
|
62
62
|
# Lazy import to avoid requiring mem0ai when not used
|
|
63
|
-
|
|
63
|
+
try:
|
|
64
|
+
from mem0 import MemoryClient
|
|
65
|
+
except ImportError as e:
|
|
66
|
+
raise ImportError(
|
|
67
|
+
"Mem0 backend requires 'mem0ai' package. Install it with: pip install gobby[mem0]"
|
|
68
|
+
) from e
|
|
64
69
|
|
|
65
70
|
self._client: MemoryClient = MemoryClient(api_key=api_key, **kwargs)
|
|
66
71
|
self._default_user_id = user_id
|