basion-agent 0.4.0__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.
- basion_agent/__init__.py +62 -0
- basion_agent/agent.py +360 -0
- basion_agent/agent_state_client.py +149 -0
- basion_agent/app.py +502 -0
- basion_agent/artifact.py +58 -0
- basion_agent/attachment_client.py +153 -0
- basion_agent/checkpoint_client.py +169 -0
- basion_agent/checkpointer.py +16 -0
- basion_agent/cli.py +139 -0
- basion_agent/conversation.py +103 -0
- basion_agent/conversation_client.py +86 -0
- basion_agent/conversation_message.py +48 -0
- basion_agent/exceptions.py +36 -0
- basion_agent/extensions/__init__.py +1 -0
- basion_agent/extensions/langgraph.py +526 -0
- basion_agent/extensions/pydantic_ai.py +180 -0
- basion_agent/gateway_client.py +531 -0
- basion_agent/gateway_pb2.py +73 -0
- basion_agent/gateway_pb2_grpc.py +101 -0
- basion_agent/heartbeat.py +84 -0
- basion_agent/loki_handler.py +355 -0
- basion_agent/memory.py +73 -0
- basion_agent/memory_client.py +155 -0
- basion_agent/message.py +333 -0
- basion_agent/py.typed +0 -0
- basion_agent/streamer.py +184 -0
- basion_agent/structural/__init__.py +6 -0
- basion_agent/structural/artifact.py +94 -0
- basion_agent/structural/base.py +71 -0
- basion_agent/structural/stepper.py +125 -0
- basion_agent/structural/surface.py +90 -0
- basion_agent/structural/text_block.py +96 -0
- basion_agent/tools/__init__.py +19 -0
- basion_agent/tools/container.py +46 -0
- basion_agent/tools/knowledge_graph.py +306 -0
- basion_agent-0.4.0.dist-info/METADATA +880 -0
- basion_agent-0.4.0.dist-info/RECORD +41 -0
- basion_agent-0.4.0.dist-info/WHEEL +5 -0
- basion_agent-0.4.0.dist-info/entry_points.txt +2 -0
- basion_agent-0.4.0.dist-info/licenses/LICENSE +21 -0
- basion_agent-0.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Artifact structural streamer for file/image/iframe artifacts."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Dict, Any
|
|
4
|
+
from .base import StructuralStreamer
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Artifact(StructuralStreamer):
|
|
9
|
+
"""Manages artifact lifecycle: generating → done.
|
|
10
|
+
|
|
11
|
+
Artifacts are files, images, iframes that are generated and displayed.
|
|
12
|
+
Artifact data is persisted to database on done().
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
artifact = Artifact() # ID auto-generated
|
|
16
|
+
s.stream_by(artifact).generating("Creating chart...", progress=0.5)
|
|
17
|
+
s.stream_by(artifact).done(
|
|
18
|
+
url="https://example.com/chart.png",
|
|
19
|
+
type="image",
|
|
20
|
+
title="Sales Chart"
|
|
21
|
+
)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def _generate_id(self) -> str:
|
|
25
|
+
return f"artifact-{uuid.uuid4().hex[:8]}"
|
|
26
|
+
|
|
27
|
+
def get_event_type_prefix(self) -> str:
|
|
28
|
+
return "artifact"
|
|
29
|
+
|
|
30
|
+
def generating(
|
|
31
|
+
self,
|
|
32
|
+
message: Optional[str] = None,
|
|
33
|
+
progress: Optional[float] = None
|
|
34
|
+
) -> 'Artifact':
|
|
35
|
+
"""Signal artifact is being generated.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
message: Status message (e.g., "Creating chart...")
|
|
39
|
+
progress: Progress value 0.0-1.0
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
self for method chaining
|
|
43
|
+
"""
|
|
44
|
+
kwargs = {}
|
|
45
|
+
if message is not None:
|
|
46
|
+
kwargs["message"] = message
|
|
47
|
+
if progress is not None:
|
|
48
|
+
kwargs["progress"] = progress
|
|
49
|
+
|
|
50
|
+
return self._send_event("generating", **kwargs)
|
|
51
|
+
|
|
52
|
+
def done(
|
|
53
|
+
self,
|
|
54
|
+
url: str,
|
|
55
|
+
type: str,
|
|
56
|
+
title: Optional[str] = None,
|
|
57
|
+
description: Optional[str] = None,
|
|
58
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
59
|
+
) -> 'Artifact':
|
|
60
|
+
"""Signal artifact is complete and persist to database.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
url: URL to artifact
|
|
64
|
+
type: Artifact type (image, iframe, document, video, audio, code, link, file)
|
|
65
|
+
title: Display title
|
|
66
|
+
description: Optional description
|
|
67
|
+
metadata: Additional metadata (size, mimeType, dimensions, etc.)
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
self for method chaining
|
|
71
|
+
"""
|
|
72
|
+
kwargs = {
|
|
73
|
+
"url": url,
|
|
74
|
+
"type": type,
|
|
75
|
+
}
|
|
76
|
+
if title:
|
|
77
|
+
kwargs["title"] = title
|
|
78
|
+
if description:
|
|
79
|
+
kwargs["description"] = description
|
|
80
|
+
if metadata:
|
|
81
|
+
kwargs["metadata"] = metadata
|
|
82
|
+
|
|
83
|
+
return self._send_event("done", **kwargs)
|
|
84
|
+
|
|
85
|
+
def error(self, message: str) -> 'Artifact':
|
|
86
|
+
"""Signal artifact generation failed.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
message: Error message
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
self for method chaining
|
|
93
|
+
"""
|
|
94
|
+
return self._send_event("error", message=message)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Base class for structural streaming objects."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Dict, Any, Optional, TYPE_CHECKING
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from ..streamer import Streamer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StructuralStreamer(ABC):
|
|
12
|
+
"""Base class for structural streaming objects.
|
|
13
|
+
|
|
14
|
+
Structural streamers generate events but don't hold streaming state.
|
|
15
|
+
The Streamer binds to them via stream_by() to enable streaming.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, id: Optional[str] = None):
|
|
19
|
+
"""Initialize with auto-generated or explicit ID."""
|
|
20
|
+
self.id = id or self._generate_id()
|
|
21
|
+
self._streamer: Optional['Streamer'] = None
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def _generate_id(self) -> str:
|
|
25
|
+
"""Generate a unique ID for this streamer type."""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def get_event_type_prefix(self) -> str:
|
|
30
|
+
"""Get the event type prefix (e.g., 'artifact', 'text_block')."""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
def _bind_streamer(self, streamer: 'Streamer') -> 'StructuralStreamer':
|
|
34
|
+
"""Bind this structural streamer to a Streamer instance.
|
|
35
|
+
|
|
36
|
+
Called by Streamer.stream_by(). Returns self for chaining.
|
|
37
|
+
"""
|
|
38
|
+
self._streamer = streamer
|
|
39
|
+
return self
|
|
40
|
+
|
|
41
|
+
def _send_event(self, action: str, **kwargs) -> 'StructuralStreamer':
|
|
42
|
+
"""Send an event through the bound streamer.
|
|
43
|
+
|
|
44
|
+
Creates event payload and sends via streamer._send().
|
|
45
|
+
Returns self for method chaining.
|
|
46
|
+
"""
|
|
47
|
+
if not self._streamer:
|
|
48
|
+
raise RuntimeError(
|
|
49
|
+
f"{self.__class__.__name__} not bound to a Streamer. "
|
|
50
|
+
f"Call streamer.stream_by() first."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Build event body
|
|
54
|
+
body = {"id": self.id, **kwargs}
|
|
55
|
+
|
|
56
|
+
# Build event type
|
|
57
|
+
event_type = f"{self.get_event_type_prefix()}:{action}"
|
|
58
|
+
|
|
59
|
+
# Send through streamer using existing _send() method
|
|
60
|
+
# content = stringified JSON body
|
|
61
|
+
# eventType = structural event type
|
|
62
|
+
# persist = False (structural events not persisted as content)
|
|
63
|
+
import json
|
|
64
|
+
self._streamer._send(
|
|
65
|
+
content=json.dumps(body),
|
|
66
|
+
done=False,
|
|
67
|
+
persist=False,
|
|
68
|
+
event_type=event_type
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
return self
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Stepper structural streamer for multi-step progress indicators."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, List
|
|
4
|
+
from .base import StructuralStreamer
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Stepper(StructuralStreamer):
|
|
9
|
+
"""Manages multi-step progress indicator.
|
|
10
|
+
|
|
11
|
+
Stepper events are NOT persisted to database.
|
|
12
|
+
IDs are auto-generated - no need to provide them manually.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
stepper = Stepper(steps=["Fetch", "Process", "Report"]) # ID auto-generated
|
|
16
|
+
s.stream_by(stepper).start_step(0)
|
|
17
|
+
s.stream_by(stepper).complete_step(0)
|
|
18
|
+
s.stream_by(stepper).start_step(1)
|
|
19
|
+
s.stream_by(stepper).add_step("Verify") # Dynamic step
|
|
20
|
+
s.stream_by(stepper).complete_step(1)
|
|
21
|
+
s.stream_by(stepper).done()
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
id: Optional[str] = None,
|
|
27
|
+
steps: Optional[List[str]] = None
|
|
28
|
+
):
|
|
29
|
+
super().__init__(id)
|
|
30
|
+
self.steps = steps or []
|
|
31
|
+
self._initialized = False
|
|
32
|
+
|
|
33
|
+
def _generate_id(self) -> str:
|
|
34
|
+
return f"stepper-{uuid.uuid4().hex[:8]}"
|
|
35
|
+
|
|
36
|
+
def get_event_type_prefix(self) -> str:
|
|
37
|
+
return "stepper"
|
|
38
|
+
|
|
39
|
+
def _ensure_initialized(self) -> 'Stepper':
|
|
40
|
+
"""Send initialization event with steps if not already sent.
|
|
41
|
+
|
|
42
|
+
This is automatically called before any other stepper event.
|
|
43
|
+
"""
|
|
44
|
+
if not self._initialized and self.steps:
|
|
45
|
+
self._send_event("init", steps=self.steps)
|
|
46
|
+
self._initialized = True
|
|
47
|
+
return self
|
|
48
|
+
|
|
49
|
+
def start_step(self, index: int) -> 'Stepper':
|
|
50
|
+
"""Mark step as in progress.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
index: Step index to start
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
self for method chaining
|
|
57
|
+
"""
|
|
58
|
+
self._ensure_initialized()
|
|
59
|
+
return self._send_event("start_step", index=index)
|
|
60
|
+
|
|
61
|
+
def complete_step(self, index: int) -> 'Stepper':
|
|
62
|
+
"""Mark step as complete.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
index: Step index to complete
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
self for method chaining
|
|
69
|
+
"""
|
|
70
|
+
self._ensure_initialized()
|
|
71
|
+
return self._send_event("complete_step", index=index)
|
|
72
|
+
|
|
73
|
+
def fail_step(self, index: int, error: Optional[str] = None) -> 'Stepper':
|
|
74
|
+
"""Mark step as failed.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
index: Step index that failed
|
|
78
|
+
error: Optional error message
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
self for method chaining
|
|
82
|
+
"""
|
|
83
|
+
self._ensure_initialized()
|
|
84
|
+
kwargs = {"index": index}
|
|
85
|
+
if error:
|
|
86
|
+
kwargs["error"] = error
|
|
87
|
+
return self._send_event("fail_step", **kwargs)
|
|
88
|
+
|
|
89
|
+
def add_step(self, label: str, index: Optional[int] = None) -> 'Stepper':
|
|
90
|
+
"""Add a new step dynamically.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
label: Step label
|
|
94
|
+
index: Optional position to insert at
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
self for method chaining
|
|
98
|
+
"""
|
|
99
|
+
self._ensure_initialized()
|
|
100
|
+
kwargs = {"label": label}
|
|
101
|
+
if index is not None:
|
|
102
|
+
kwargs["index"] = index
|
|
103
|
+
return self._send_event("add_step", **kwargs)
|
|
104
|
+
|
|
105
|
+
def update_step_label(self, index: int, label: str) -> 'Stepper':
|
|
106
|
+
"""Update step label.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
index: Step index to update
|
|
110
|
+
label: New label
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
self for method chaining
|
|
114
|
+
"""
|
|
115
|
+
self._ensure_initialized()
|
|
116
|
+
return self._send_event("update_step_label", index=index, label=label)
|
|
117
|
+
|
|
118
|
+
def done(self) -> 'Stepper':
|
|
119
|
+
"""Mark stepper as complete (all steps finished).
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
self for method chaining
|
|
123
|
+
"""
|
|
124
|
+
self._ensure_initialized()
|
|
125
|
+
return self._send_event("done")
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Surface structural streamer for embedded interactive components."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Dict, Any
|
|
4
|
+
from .base import StructuralStreamer
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Surface(StructuralStreamer):
|
|
9
|
+
"""Manages embedded surface lifecycle (identical to Artifact).
|
|
10
|
+
|
|
11
|
+
Surfaces are embedded interactive components (iframes, embeds).
|
|
12
|
+
Surface data is persisted to database as artifacts with surface type.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
surface = Surface()
|
|
16
|
+
s.stream_by(surface).generating("Loading calendar...")
|
|
17
|
+
s.stream_by(surface).done(url="https://cal.com/embed", type="iframe")
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def _generate_id(self) -> str:
|
|
21
|
+
return f"surface-{uuid.uuid4().hex[:8]}"
|
|
22
|
+
|
|
23
|
+
def get_event_type_prefix(self) -> str:
|
|
24
|
+
return "surface"
|
|
25
|
+
|
|
26
|
+
def generating(
|
|
27
|
+
self,
|
|
28
|
+
message: Optional[str] = None,
|
|
29
|
+
progress: Optional[float] = None
|
|
30
|
+
) -> 'Surface':
|
|
31
|
+
"""Signal surface is loading.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
message: Status message (e.g., "Loading calendar...")
|
|
35
|
+
progress: Progress value 0.0-1.0
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
self for method chaining
|
|
39
|
+
"""
|
|
40
|
+
kwargs = {}
|
|
41
|
+
if message is not None:
|
|
42
|
+
kwargs["message"] = message
|
|
43
|
+
if progress is not None:
|
|
44
|
+
kwargs["progress"] = progress
|
|
45
|
+
|
|
46
|
+
return self._send_event("generating", **kwargs)
|
|
47
|
+
|
|
48
|
+
def done(
|
|
49
|
+
self,
|
|
50
|
+
url: str,
|
|
51
|
+
type: str = "iframe",
|
|
52
|
+
title: Optional[str] = None,
|
|
53
|
+
description: Optional[str] = None,
|
|
54
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
55
|
+
) -> 'Surface':
|
|
56
|
+
"""Signal surface is ready and persist to database.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
url: URL to surface
|
|
60
|
+
type: Surface type (default: iframe)
|
|
61
|
+
title: Display title
|
|
62
|
+
description: Optional description
|
|
63
|
+
metadata: Additional metadata
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
self for method chaining
|
|
67
|
+
"""
|
|
68
|
+
kwargs = {
|
|
69
|
+
"url": url,
|
|
70
|
+
"type": type,
|
|
71
|
+
}
|
|
72
|
+
if title:
|
|
73
|
+
kwargs["title"] = title
|
|
74
|
+
if description:
|
|
75
|
+
kwargs["description"] = description
|
|
76
|
+
if metadata:
|
|
77
|
+
kwargs["metadata"] = metadata
|
|
78
|
+
|
|
79
|
+
return self._send_event("done", **kwargs)
|
|
80
|
+
|
|
81
|
+
def error(self, message: str) -> 'Surface':
|
|
82
|
+
"""Signal surface loading failed.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
message: Error message
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
self for method chaining
|
|
89
|
+
"""
|
|
90
|
+
return self._send_event("error", message=message)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""TextBlock structural streamer for collapsible text content."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from .base import StructuralStreamer
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TextBlock(StructuralStreamer):
|
|
9
|
+
"""Manages collapsible text block with title and body.
|
|
10
|
+
|
|
11
|
+
Both title and body support streaming (append) and update (replace).
|
|
12
|
+
TextBlock events are NOT persisted to database.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
block = TextBlock() # ID auto-generated
|
|
16
|
+
s.stream_by(block).set_variant("thinking")
|
|
17
|
+
s.stream_by(block).stream_title("Deep ")
|
|
18
|
+
s.stream_by(block).stream_title("Analysis...")
|
|
19
|
+
s.stream_by(block).stream_body("Step 1: Check patterns\\n")
|
|
20
|
+
s.stream_by(block).stream_body("Step 2: Validate\\n")
|
|
21
|
+
s.stream_by(block).update_title("Analysis Complete")
|
|
22
|
+
s.stream_by(block).done()
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, id: Optional[str] = None, title: Optional[str] = None):
|
|
26
|
+
super().__init__(id)
|
|
27
|
+
self.initial_title = title
|
|
28
|
+
|
|
29
|
+
def _generate_id(self) -> str:
|
|
30
|
+
return f"text_block-{uuid.uuid4().hex[:8]}"
|
|
31
|
+
|
|
32
|
+
def get_event_type_prefix(self) -> str:
|
|
33
|
+
return "text_block"
|
|
34
|
+
|
|
35
|
+
def stream_title(self, content: str) -> 'TextBlock':
|
|
36
|
+
"""Stream title content (appends to existing).
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
content: Content to append to title
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
self for method chaining
|
|
43
|
+
"""
|
|
44
|
+
return self._send_event("stream_title", content=content)
|
|
45
|
+
|
|
46
|
+
def stream_body(self, content: str) -> 'TextBlock':
|
|
47
|
+
"""Stream body content (appends to existing).
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
content: Content to append to body
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
self for method chaining
|
|
54
|
+
"""
|
|
55
|
+
return self._send_event("stream_body", content=content)
|
|
56
|
+
|
|
57
|
+
def update_title(self, title: str) -> 'TextBlock':
|
|
58
|
+
"""Replace entire title.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
title: New title (replaces existing)
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
self for method chaining
|
|
65
|
+
"""
|
|
66
|
+
return self._send_event("update_title", title=title)
|
|
67
|
+
|
|
68
|
+
def update_body(self, body: str) -> 'TextBlock':
|
|
69
|
+
"""Replace entire body.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
body: New body (replaces existing)
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
self for method chaining
|
|
76
|
+
"""
|
|
77
|
+
return self._send_event("update_body", body=body)
|
|
78
|
+
|
|
79
|
+
def set_variant(self, variant: str) -> 'TextBlock':
|
|
80
|
+
"""Set visual variant.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
variant: Visual style (thinking, note, warning, error, success)
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
self for method chaining
|
|
87
|
+
"""
|
|
88
|
+
return self._send_event("set_variant", variant=variant)
|
|
89
|
+
|
|
90
|
+
def done(self) -> 'TextBlock':
|
|
91
|
+
"""Mark text block as complete.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
self for method chaining
|
|
95
|
+
"""
|
|
96
|
+
return self._send_event("done")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Tools module for Basion Agent SDK."""
|
|
2
|
+
|
|
3
|
+
from .container import Tools
|
|
4
|
+
from .knowledge_graph import (
|
|
5
|
+
Edge,
|
|
6
|
+
Entity,
|
|
7
|
+
KnowledgeGraphTool,
|
|
8
|
+
PathStep,
|
|
9
|
+
SimilarDisease,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"Tools",
|
|
14
|
+
"KnowledgeGraphTool",
|
|
15
|
+
"Entity",
|
|
16
|
+
"Edge",
|
|
17
|
+
"SimilarDisease",
|
|
18
|
+
"PathStep",
|
|
19
|
+
]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Tools container - accessed via agent.tools property."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from .knowledge_graph import KnowledgeGraphTool
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Tools:
|
|
10
|
+
"""
|
|
11
|
+
Container for all available tools.
|
|
12
|
+
|
|
13
|
+
Accessed via agent.tools property (similar to agent.streamer(message)):
|
|
14
|
+
kg = agent.tools.knowledge_graph
|
|
15
|
+
diseases = await kg.search_diseases(name="Huntington")
|
|
16
|
+
|
|
17
|
+
Tools are lazy-initialized - no overhead if not used.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, gateway_client):
|
|
21
|
+
self._gateway_client = gateway_client
|
|
22
|
+
self._knowledge_graph: Optional["KnowledgeGraphTool"] = None
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def knowledge_graph(self) -> "KnowledgeGraphTool":
|
|
26
|
+
"""
|
|
27
|
+
Knowledge Graph tool for querying biomedical entities and relationships.
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
kg = agent.tools.knowledge_graph
|
|
31
|
+
diseases = await kg.search_diseases(name="Huntington")
|
|
32
|
+
similar = await kg.find_similar_diseases("Huntington Disease")
|
|
33
|
+
path = await kg.find_shortest_path("BRCA1", "protein", "Breast Cancer", "disease")
|
|
34
|
+
"""
|
|
35
|
+
if self._knowledge_graph is None:
|
|
36
|
+
from .knowledge_graph import KnowledgeGraphTool
|
|
37
|
+
|
|
38
|
+
self._knowledge_graph = KnowledgeGraphTool(
|
|
39
|
+
self._gateway_client.knowledge_graph_url
|
|
40
|
+
)
|
|
41
|
+
return self._knowledge_graph
|
|
42
|
+
|
|
43
|
+
async def close(self):
|
|
44
|
+
"""Close all tool sessions."""
|
|
45
|
+
if self._knowledge_graph:
|
|
46
|
+
await self._knowledge_graph.close()
|