idun-agent-engine 0.4.0__py3-none-any.whl → 0.4.2__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.
- idun_agent_engine/_version.py +1 -1
- idun_agent_engine/agent/adk/adk.py +7 -4
- idun_agent_engine/agent/haystack/__init__.py +0 -2
- idun_agent_engine/agent/haystack/haystack.py +9 -5
- idun_agent_engine/agent/langgraph/langgraph.py +10 -13
- idun_agent_engine/core/config_builder.py +33 -13
- idun_agent_engine/guardrails/guardrails_hub/guardrails_hub.py +52 -9
- idun_agent_engine/mcp/__init__.py +2 -2
- idun_agent_engine/mcp/helpers.py +53 -15
- idun_agent_engine/mcp/registry.py +5 -5
- idun_agent_engine/observability/base.py +11 -2
- idun_agent_engine/observability/gcp_trace/gcp_trace_handler.py +3 -1
- idun_agent_engine/observability/langfuse/langfuse_handler.py +1 -3
- idun_agent_engine/server/dependencies.py +7 -2
- idun_agent_engine/server/lifespan.py +2 -7
- idun_agent_engine/server/routers/agent.py +2 -1
- idun_agent_engine/server/routers/base.py +7 -5
- idun_agent_engine/telemetry/__init__.py +0 -1
- idun_agent_engine/telemetry/config.py +0 -1
- idun_agent_engine/telemetry/telemetry.py +3 -4
- idun_agent_engine/templates/correction.py +4 -7
- idun_agent_engine/templates/deep_research.py +1 -0
- idun_agent_engine/templates/translation.py +4 -4
- {idun_agent_engine-0.4.0.dist-info → idun_agent_engine-0.4.2.dist-info}/METADATA +2 -2
- idun_agent_engine-0.4.2.dist-info/RECORD +86 -0
- idun_platform_cli/groups/agent/package.py +4 -1
- idun_platform_cli/groups/agent/serve.py +2 -0
- idun_platform_cli/groups/init.py +2 -0
- idun_platform_cli/telemetry.py +55 -0
- idun_platform_cli/tui/css/create_agent.py +137 -14
- idun_platform_cli/tui/css/main.py +7 -10
- idun_platform_cli/tui/main.py +3 -3
- idun_platform_cli/tui/schemas/create_agent.py +8 -4
- idun_platform_cli/tui/screens/create_agent.py +186 -20
- idun_platform_cli/tui/utils/config.py +23 -2
- idun_platform_cli/tui/validators/guardrails.py +20 -6
- idun_platform_cli/tui/validators/mcps.py +9 -6
- idun_platform_cli/tui/widgets/__init__.py +8 -4
- idun_platform_cli/tui/widgets/chat_widget.py +155 -0
- idun_platform_cli/tui/widgets/guardrails_widget.py +12 -4
- idun_platform_cli/tui/widgets/identity_widget.py +28 -10
- idun_platform_cli/tui/widgets/mcps_widget.py +113 -25
- idun_platform_cli/tui/widgets/memory_widget.py +194 -0
- idun_platform_cli/tui/widgets/observability_widget.py +12 -14
- idun_platform_cli/tui/widgets/serve_widget.py +50 -47
- idun_agent_engine/agent/haystack/haystack_model.py +0 -13
- idun_agent_engine/guardrails/guardrails_hub/utils.py +0 -1
- idun_agent_engine/server/routers/agui.py +0 -47
- idun_agent_engine-0.4.0.dist-info/RECORD +0 -86
- {idun_agent_engine-0.4.0.dist-info → idun_agent_engine-0.4.2.dist-info}/WHEEL +0 -0
- {idun_agent_engine-0.4.0.dist-info → idun_agent_engine-0.4.2.dist-info}/entry_points.txt +0 -0
idun_platform_cli/tui/main.py
CHANGED
|
@@ -59,14 +59,14 @@ class IdunApp(App):
|
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
yield Static("IDUN AGENT PLATFORM", classes="platform")
|
|
62
|
-
yield Static("Deploy, guard and monitor
|
|
63
|
-
yield Static("Built with
|
|
62
|
+
yield Static("Deploy, guard and monitor your agents.", classes="tagline")
|
|
63
|
+
yield Static("Built with 💜 by Idun Group", classes="built-by")
|
|
64
64
|
yield Static(f"v{__version__}", classes="version")
|
|
65
65
|
with Horizontal(classes="link-container"):
|
|
66
66
|
yield Link("⭐️ Github", url=self.REPO, classes="links")
|
|
67
67
|
yield Link("📚 Docs", url=self.DOCS, classes="links")
|
|
68
|
+
yield Link("🌐 Website", url=self.DOCS, classes="links")
|
|
68
69
|
|
|
69
|
-
yield Static("What do you want to do?", classes="question_prompt")
|
|
70
70
|
yield MainPageActions()
|
|
71
71
|
yield Footer()
|
|
72
72
|
|
|
@@ -21,7 +21,7 @@ agent:
|
|
|
21
21
|
# we need to map the graph_definition, pipeline_definition, agent_definition fields based on framework
|
|
22
22
|
AGENT_SOURCE_KEY_MAPPING: dict[str, str] = dict(
|
|
23
23
|
{
|
|
24
|
-
"HAYSTACK": "
|
|
24
|
+
"HAYSTACK": "component_definition",
|
|
25
25
|
"LANGGRAPH": "graph_definition",
|
|
26
26
|
"ADK": "agent",
|
|
27
27
|
}
|
|
@@ -35,21 +35,25 @@ class TUIAgentConfig(BaseModel):
|
|
|
35
35
|
graph_definition: str
|
|
36
36
|
|
|
37
37
|
@field_validator("*", mode="after")
|
|
38
|
-
def validate_not_null(cls, value: str | Any | None) -> str:
|
|
38
|
+
def validate_not_null(cls, value: str | Any | None) -> str: # noqa: N805
|
|
39
39
|
if value is None or value == "":
|
|
40
40
|
raise ValueError("Cannot have empty fields!")
|
|
41
41
|
return value
|
|
42
42
|
|
|
43
43
|
def to_engine_config(self) -> dict[str, Any]:
|
|
44
|
+
sanitized_name = self.name.replace("-", "_").replace(" ", "_")
|
|
45
|
+
|
|
44
46
|
agent_config = {
|
|
45
|
-
"name": self.name,
|
|
47
|
+
"name": sanitized_name if self.framework == "ADK" else self.name,
|
|
46
48
|
AGENT_SOURCE_KEY_MAPPING[self.framework]: self.graph_definition,
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
if self.framework == "ADK":
|
|
50
|
-
agent_config["app_name"] =
|
|
52
|
+
agent_config["app_name"] = sanitized_name
|
|
51
53
|
agent_config["session_service"] = {"type": "in_memory"}
|
|
52
54
|
agent_config["memory_service"] = {"type": "in_memory"}
|
|
55
|
+
elif self.framework == "HAYSTACK":
|
|
56
|
+
agent_config["component_type"] = "pipeline"
|
|
53
57
|
|
|
54
58
|
return {
|
|
55
59
|
"server": {"api": {"port": self.port}},
|
|
@@ -7,15 +7,17 @@ from textual.app import ComposeResult
|
|
|
7
7
|
from textual.containers import Container, Horizontal, Vertical
|
|
8
8
|
from textual.reactive import reactive
|
|
9
9
|
from textual.screen import Screen
|
|
10
|
-
from textual.widgets import Button,
|
|
10
|
+
from textual.widgets import Button, Label, RichLog, Static
|
|
11
11
|
|
|
12
12
|
from idun_platform_cli.tui.css.create_agent import CREATE_AGENT_CSS
|
|
13
13
|
from idun_platform_cli.tui.schemas.create_agent import TUIAgentConfig
|
|
14
14
|
from idun_platform_cli.tui.utils.config import ConfigManager
|
|
15
15
|
from idun_platform_cli.tui.widgets import (
|
|
16
|
+
ChatWidget,
|
|
16
17
|
GuardrailsWidget,
|
|
17
18
|
IdentityWidget,
|
|
18
19
|
MCPsWidget,
|
|
20
|
+
MemoryWidget,
|
|
19
21
|
ObservabilityWidget,
|
|
20
22
|
ServeWidget,
|
|
21
23
|
)
|
|
@@ -32,10 +34,12 @@ class CreateAgentScreen(Screen):
|
|
|
32
34
|
active_section = reactive("identity")
|
|
33
35
|
nav_panes = [
|
|
34
36
|
"nav-identity",
|
|
37
|
+
"nav-memory",
|
|
35
38
|
"nav-observability",
|
|
36
39
|
"nav-guardrails",
|
|
37
40
|
"nav-mcps",
|
|
38
41
|
"nav-serve",
|
|
42
|
+
"nav-chat",
|
|
39
43
|
]
|
|
40
44
|
current_nav_index = 0
|
|
41
45
|
focus_on_nav = True # Track if focus is on nav or content
|
|
@@ -48,6 +52,39 @@ class CreateAgentScreen(Screen):
|
|
|
48
52
|
self.server_process = None
|
|
49
53
|
self.server_running = False
|
|
50
54
|
|
|
55
|
+
def on_unmount(self) -> None:
|
|
56
|
+
if self.server_process:
|
|
57
|
+
import os
|
|
58
|
+
import signal
|
|
59
|
+
import subprocess
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
pgid = os.getpgid(self.server_process.pid)
|
|
63
|
+
os.killpg(pgid, signal.SIGKILL)
|
|
64
|
+
except (ProcessLookupError, OSError, AttributeError, PermissionError):
|
|
65
|
+
try:
|
|
66
|
+
self.server_process.kill()
|
|
67
|
+
except:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
config = self.config_manager.load_config()
|
|
72
|
+
if config:
|
|
73
|
+
port = config.get("server", {}).get("api", {}).get("port", 8008)
|
|
74
|
+
pids = (
|
|
75
|
+
subprocess.check_output(["lsof", "-ti", f":{port}"], text=True)
|
|
76
|
+
.strip()
|
|
77
|
+
.split("\n")
|
|
78
|
+
)
|
|
79
|
+
for pid in pids:
|
|
80
|
+
if pid:
|
|
81
|
+
try:
|
|
82
|
+
os.kill(int(pid), signal.SIGKILL)
|
|
83
|
+
except:
|
|
84
|
+
pass
|
|
85
|
+
except:
|
|
86
|
+
pass
|
|
87
|
+
|
|
51
88
|
def watch_active_section(self, new_section: str) -> None:
|
|
52
89
|
for section_id, widget in self.widgets_map.items():
|
|
53
90
|
if section_id == new_section:
|
|
@@ -67,12 +104,20 @@ class CreateAgentScreen(Screen):
|
|
|
67
104
|
severity="warning",
|
|
68
105
|
)
|
|
69
106
|
|
|
107
|
+
if new_section == "chat":
|
|
108
|
+
config = self.config_manager.load_config()
|
|
109
|
+
chat_widget = self.widgets_map.get("chat")
|
|
110
|
+
if chat_widget and config:
|
|
111
|
+
chat_widget.load_config(config)
|
|
112
|
+
|
|
70
113
|
for pane_id in [
|
|
71
114
|
"nav-identity",
|
|
115
|
+
"nav-memory",
|
|
72
116
|
"nav-observability",
|
|
73
117
|
"nav-guardrails",
|
|
74
118
|
"nav-mcps",
|
|
75
119
|
"nav-serve",
|
|
120
|
+
"nav-chat",
|
|
76
121
|
]:
|
|
77
122
|
pane = self.query_one(f"#{pane_id}")
|
|
78
123
|
if pane_id == f"nav-{new_section}":
|
|
@@ -101,6 +146,18 @@ class CreateAgentScreen(Screen):
|
|
|
101
146
|
nav_identity.can_focus = True
|
|
102
147
|
yield nav_identity
|
|
103
148
|
|
|
149
|
+
nav_memory = Vertical(
|
|
150
|
+
Label(
|
|
151
|
+
"Configure agent\ncheckpointing",
|
|
152
|
+
id="nav-memory-label",
|
|
153
|
+
),
|
|
154
|
+
classes="nav-pane",
|
|
155
|
+
id="nav-memory",
|
|
156
|
+
)
|
|
157
|
+
nav_memory.border_title = "Memory"
|
|
158
|
+
nav_memory.can_focus = True
|
|
159
|
+
yield nav_memory
|
|
160
|
+
|
|
104
161
|
nav_observability = Vertical(
|
|
105
162
|
Label(
|
|
106
163
|
"Setup monitoring\nand tracing",
|
|
@@ -140,6 +197,15 @@ class CreateAgentScreen(Screen):
|
|
|
140
197
|
nav_serve.can_focus = True
|
|
141
198
|
yield nav_serve
|
|
142
199
|
|
|
200
|
+
nav_chat = Vertical(
|
|
201
|
+
Label("Chat with your\nrunning agent"),
|
|
202
|
+
classes="nav-pane",
|
|
203
|
+
id="nav-chat",
|
|
204
|
+
)
|
|
205
|
+
nav_chat.border_title = "Chat"
|
|
206
|
+
nav_chat.can_focus = True
|
|
207
|
+
yield nav_chat
|
|
208
|
+
|
|
143
209
|
with Horizontal(classes="action-buttons"):
|
|
144
210
|
yield Button("Back", id="back_button", classes="action-btn")
|
|
145
211
|
yield Button("Next", id="next_button", classes="action-btn")
|
|
@@ -147,6 +213,9 @@ class CreateAgentScreen(Screen):
|
|
|
147
213
|
with Vertical(classes="content-area"):
|
|
148
214
|
identity = IdentityWidget(id="widget-identity", classes="section")
|
|
149
215
|
|
|
216
|
+
memory = MemoryWidget(id="widget-memory", classes="section")
|
|
217
|
+
memory.border_title = "Memory & Checkpointing"
|
|
218
|
+
|
|
150
219
|
observability = ObservabilityWidget(
|
|
151
220
|
id="widget-observability", classes="section"
|
|
152
221
|
)
|
|
@@ -161,26 +230,38 @@ class CreateAgentScreen(Screen):
|
|
|
161
230
|
serve = ServeWidget(id="widget-serve", classes="section")
|
|
162
231
|
serve.border_title = "Validate & Run"
|
|
163
232
|
|
|
233
|
+
chat = ChatWidget(id="widget-chat", classes="section")
|
|
234
|
+
chat.border_title = "Chat"
|
|
235
|
+
|
|
164
236
|
self.widgets_map = {
|
|
165
237
|
"identity": identity,
|
|
238
|
+
"memory": memory,
|
|
166
239
|
"observability": observability,
|
|
167
240
|
"guardrails": guardrails,
|
|
168
241
|
"mcps": mcps,
|
|
169
242
|
"serve": serve,
|
|
243
|
+
"chat": chat,
|
|
170
244
|
}
|
|
171
245
|
|
|
246
|
+
memory.display = False
|
|
172
247
|
observability.display = False
|
|
173
248
|
guardrails.display = False
|
|
174
249
|
mcps.display = False
|
|
175
250
|
serve.display = False
|
|
251
|
+
chat.display = False
|
|
176
252
|
|
|
177
253
|
yield identity
|
|
254
|
+
yield memory
|
|
178
255
|
yield observability
|
|
179
256
|
yield guardrails
|
|
180
257
|
yield mcps
|
|
181
258
|
yield serve
|
|
259
|
+
yield chat
|
|
182
260
|
|
|
183
|
-
footer = Static(
|
|
261
|
+
footer = Static(
|
|
262
|
+
"💡 Press Next to save section | Press Ctrl+Q to exit",
|
|
263
|
+
classes="custom-footer",
|
|
264
|
+
)
|
|
184
265
|
yield footer
|
|
185
266
|
|
|
186
267
|
def on_mount(self) -> None:
|
|
@@ -201,7 +282,7 @@ class CreateAgentScreen(Screen):
|
|
|
201
282
|
if active_widget:
|
|
202
283
|
try:
|
|
203
284
|
focusable = active_widget.query(
|
|
204
|
-
"Input, OptionList, DirectoryTree, Button"
|
|
285
|
+
"Input, OptionList, DirectoryTree, Button, RadioSet"
|
|
205
286
|
).first()
|
|
206
287
|
if focusable:
|
|
207
288
|
focusable.focus()
|
|
@@ -288,6 +369,8 @@ class CreateAgentScreen(Screen):
|
|
|
288
369
|
return
|
|
289
370
|
|
|
290
371
|
if section == "identity":
|
|
372
|
+
if not widget.validate():
|
|
373
|
+
return
|
|
291
374
|
data = widget.get_data()
|
|
292
375
|
if data is None:
|
|
293
376
|
self.notify("Please complete all required fields", severity="error")
|
|
@@ -307,13 +390,26 @@ class CreateAgentScreen(Screen):
|
|
|
307
390
|
return
|
|
308
391
|
self.validated_sections.add("identity")
|
|
309
392
|
self._update_nav_checkmark("identity")
|
|
310
|
-
except ValidationError
|
|
393
|
+
except ValidationError:
|
|
311
394
|
self.notify(
|
|
312
|
-
|
|
395
|
+
"Error validating Identity: make sure all fields are correct.",
|
|
313
396
|
severity="error",
|
|
314
397
|
)
|
|
315
398
|
return
|
|
316
399
|
|
|
400
|
+
elif section == "memory":
|
|
401
|
+
data = widget.get_data()
|
|
402
|
+
if data is not None:
|
|
403
|
+
success, msg = self.config_manager.save_partial("memory", data)
|
|
404
|
+
if not success:
|
|
405
|
+
self.notify("Memory configuration is invalid", severity="error")
|
|
406
|
+
return
|
|
407
|
+
self.validated_sections.add("memory")
|
|
408
|
+
self._update_nav_checkmark("memory")
|
|
409
|
+
else:
|
|
410
|
+
self.notify("Please configure checkpoint settings", severity="error")
|
|
411
|
+
return
|
|
412
|
+
|
|
317
413
|
elif section == "observability":
|
|
318
414
|
data = widget.get_data()
|
|
319
415
|
if data is None:
|
|
@@ -356,15 +452,20 @@ class CreateAgentScreen(Screen):
|
|
|
356
452
|
|
|
357
453
|
validated_servers, msg = validate_mcp_servers(data)
|
|
358
454
|
if validated_servers is None:
|
|
359
|
-
|
|
360
|
-
|
|
455
|
+
self.notify(
|
|
456
|
+
"Error validating MCPs: make sure all fields are correct.",
|
|
457
|
+
severity="error",
|
|
458
|
+
)
|
|
361
459
|
return
|
|
362
460
|
|
|
363
461
|
success, save_msg = self.config_manager.save_partial(
|
|
364
462
|
"mcp_servers", validated_servers
|
|
365
463
|
)
|
|
366
464
|
if not success:
|
|
367
|
-
self.notify(
|
|
465
|
+
self.notify(
|
|
466
|
+
"Error saving MCPs: make sure all fields are correct.",
|
|
467
|
+
severity="error",
|
|
468
|
+
)
|
|
368
469
|
return
|
|
369
470
|
|
|
370
471
|
self.validated_sections.add("mcps")
|
|
@@ -378,19 +479,74 @@ class CreateAgentScreen(Screen):
|
|
|
378
479
|
section = self.nav_panes[self.current_nav_index].replace("nav-", "")
|
|
379
480
|
self.active_section = section
|
|
380
481
|
|
|
381
|
-
elif event.button.id == "
|
|
482
|
+
elif event.button.id == "save_exit_button":
|
|
483
|
+
if self.server_running and self.server_process:
|
|
484
|
+
self.notify("Kill Server before exiting", severity="warning")
|
|
485
|
+
return
|
|
486
|
+
self.notify("Configuration saved. Exiting...", severity="information")
|
|
487
|
+
self.app.exit()
|
|
488
|
+
|
|
489
|
+
elif event.button.id == "save_run_button":
|
|
382
490
|
if self.server_running and self.server_process:
|
|
383
|
-
|
|
491
|
+
import os
|
|
492
|
+
import signal
|
|
493
|
+
import subprocess
|
|
494
|
+
|
|
495
|
+
rich_log = self.query_one("#server_logs", RichLog)
|
|
496
|
+
rich_log.write("\n[yellow]Stopping server...[/yellow]")
|
|
497
|
+
|
|
498
|
+
try:
|
|
499
|
+
pgid = os.getpgid(self.server_process.pid)
|
|
500
|
+
os.killpg(pgid, signal.SIGTERM)
|
|
501
|
+
try:
|
|
502
|
+
self.server_process.wait(timeout=3)
|
|
503
|
+
except subprocess.TimeoutExpired:
|
|
504
|
+
os.killpg(pgid, signal.SIGKILL)
|
|
505
|
+
self.server_process.wait(timeout=1)
|
|
506
|
+
except (ProcessLookupError, OSError, PermissionError):
|
|
507
|
+
try:
|
|
508
|
+
self.server_process.terminate()
|
|
509
|
+
self.server_process.wait(timeout=2)
|
|
510
|
+
except subprocess.TimeoutExpired:
|
|
511
|
+
self.server_process.kill()
|
|
512
|
+
self.server_process.wait(timeout=1)
|
|
513
|
+
except:
|
|
514
|
+
pass
|
|
515
|
+
|
|
516
|
+
config = self.config_manager.load_config()
|
|
517
|
+
if config:
|
|
518
|
+
port = config.get("server", {}).get("api", {}).get("port", 8008)
|
|
519
|
+
try:
|
|
520
|
+
subprocess.run(
|
|
521
|
+
["lsof", "-ti", f":{port}"],
|
|
522
|
+
capture_output=True,
|
|
523
|
+
text=True,
|
|
524
|
+
check=True,
|
|
525
|
+
)
|
|
526
|
+
pids = (
|
|
527
|
+
subprocess.check_output(
|
|
528
|
+
["lsof", "-ti", f":{port}"], text=True
|
|
529
|
+
)
|
|
530
|
+
.strip()
|
|
531
|
+
.split("\n")
|
|
532
|
+
)
|
|
533
|
+
for pid in pids:
|
|
534
|
+
if pid:
|
|
535
|
+
try:
|
|
536
|
+
os.kill(int(pid), signal.SIGKILL)
|
|
537
|
+
except:
|
|
538
|
+
pass
|
|
539
|
+
except:
|
|
540
|
+
pass
|
|
541
|
+
|
|
384
542
|
self.server_process = None
|
|
385
543
|
self.server_running = False
|
|
386
544
|
|
|
387
|
-
button = self.query_one("#
|
|
388
|
-
button.label = "
|
|
545
|
+
button = self.query_one("#save_run_button", Button)
|
|
546
|
+
button.label = "Save and Run"
|
|
389
547
|
button.remove_class("kill-mode")
|
|
390
548
|
|
|
391
|
-
rich_log = self.query_one("#server_logs", RichLog)
|
|
392
549
|
rich_log.write("\n[red]Server stopped[/red]")
|
|
393
|
-
|
|
394
550
|
self.notify("Server stopped", severity="information")
|
|
395
551
|
return
|
|
396
552
|
|
|
@@ -424,11 +580,18 @@ class CreateAgentScreen(Screen):
|
|
|
424
580
|
|
|
425
581
|
try:
|
|
426
582
|
process = subprocess.Popen(
|
|
427
|
-
[
|
|
583
|
+
[
|
|
584
|
+
"idun",
|
|
585
|
+
"agent",
|
|
586
|
+
"serve",
|
|
587
|
+
"--source=file",
|
|
588
|
+
f"--path={config_path}",
|
|
589
|
+
],
|
|
428
590
|
stdout=subprocess.PIPE,
|
|
429
591
|
stderr=subprocess.STDOUT,
|
|
430
592
|
text=True,
|
|
431
593
|
bufsize=1,
|
|
594
|
+
start_new_session=True,
|
|
432
595
|
)
|
|
433
596
|
|
|
434
597
|
fd = process.stdout.fileno()
|
|
@@ -438,14 +601,17 @@ class CreateAgentScreen(Screen):
|
|
|
438
601
|
self.server_process = process
|
|
439
602
|
self.server_running = True
|
|
440
603
|
|
|
441
|
-
button = self.query_one("#
|
|
604
|
+
button = self.query_one("#save_run_button", Button)
|
|
442
605
|
button.label = "Kill Server"
|
|
443
606
|
button.add_class("kill-mode")
|
|
444
607
|
|
|
445
608
|
self.run_worker(self._stream_logs(process), exclusive=True)
|
|
446
609
|
|
|
447
|
-
except Exception
|
|
448
|
-
self.notify(
|
|
610
|
+
except Exception:
|
|
611
|
+
self.notify(
|
|
612
|
+
"Failed to start server. Check your configuration.",
|
|
613
|
+
severity="error",
|
|
614
|
+
)
|
|
449
615
|
|
|
450
616
|
async def _stream_logs(self, process) -> None:
|
|
451
617
|
import asyncio
|
|
@@ -469,8 +635,8 @@ class CreateAgentScreen(Screen):
|
|
|
469
635
|
|
|
470
636
|
self.server_running = False
|
|
471
637
|
self.server_process = None
|
|
472
|
-
button = self.query_one("#
|
|
473
|
-
button.label = "
|
|
638
|
+
button = self.query_one("#save_run_button", Button)
|
|
639
|
+
button.label = "Save and Run"
|
|
474
640
|
button.remove_class("kill-mode")
|
|
475
641
|
rich_log.write("\n[yellow]Server exited[/yellow]")
|
|
476
642
|
break
|
|
@@ -7,7 +7,6 @@ from idun_agent_schema.engine.observability_v2 import ObservabilityConfig
|
|
|
7
7
|
from pydantic import ValidationError
|
|
8
8
|
|
|
9
9
|
from idun_platform_cli.tui.schemas.create_agent import (
|
|
10
|
-
AGENT_SOURCE_KEY_MAPPING,
|
|
11
10
|
TUIAgentConfig,
|
|
12
11
|
)
|
|
13
12
|
|
|
@@ -128,6 +127,28 @@ class ConfigManager:
|
|
|
128
127
|
existing_config["mcp_servers"] = mcp_servers_list
|
|
129
128
|
else:
|
|
130
129
|
existing_config["mcp_servers"] = data
|
|
130
|
+
elif section == "memory":
|
|
131
|
+
from idun_agent_schema.engine.langgraph import CheckpointConfig
|
|
132
|
+
|
|
133
|
+
if "agent" not in existing_config:
|
|
134
|
+
return False, "Agent configuration not found. Save identity first."
|
|
135
|
+
|
|
136
|
+
agent_type = existing_config.get("agent", {}).get("type")
|
|
137
|
+
if agent_type != "LANGGRAPH":
|
|
138
|
+
return (
|
|
139
|
+
True,
|
|
140
|
+
"Checkpoint configuration skipped for non-LANGGRAPH agents",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
if isinstance(data, CheckpointConfig):
|
|
144
|
+
checkpoint_dict = data.model_dump(by_alias=False, mode="json")
|
|
145
|
+
|
|
146
|
+
if "config" not in existing_config["agent"]:
|
|
147
|
+
existing_config["agent"]["config"] = {}
|
|
148
|
+
|
|
149
|
+
existing_config["agent"]["config"]["checkpointer"] = checkpoint_dict
|
|
150
|
+
else:
|
|
151
|
+
return False, "Invalid checkpoint configuration type"
|
|
131
152
|
else:
|
|
132
153
|
existing_config[section] = data
|
|
133
154
|
|
|
@@ -157,5 +178,5 @@ class ConfigManager:
|
|
|
157
178
|
with self.agent_path.open("r") as f:
|
|
158
179
|
return yaml.safe_load(f) or {}
|
|
159
180
|
|
|
160
|
-
except Exception
|
|
181
|
+
except Exception:
|
|
161
182
|
return {}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""Guardrails validation logic."""
|
|
2
2
|
|
|
3
3
|
from idun_agent_schema.engine.guardrails_v2 import (
|
|
4
|
-
|
|
4
|
+
BanListConfig,
|
|
5
5
|
BiasCheckConfig,
|
|
6
|
-
ToxicLanguageConfig,
|
|
7
6
|
CompetitionCheckConfig,
|
|
8
|
-
BanListConfig,
|
|
9
7
|
DetectPIIConfig,
|
|
8
|
+
GuardrailConfigId,
|
|
9
|
+
ToxicLanguageConfig,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
|
|
@@ -16,14 +16,22 @@ def validate_guardrail(guardrail_id: str, config: dict) -> tuple[any, str]:
|
|
|
16
16
|
case "bias_check":
|
|
17
17
|
threshold = float(config.get("threshold", 0.5))
|
|
18
18
|
validated = BiasCheckConfig(
|
|
19
|
-
config_id=GuardrailConfigId.BIAS_CHECK,
|
|
19
|
+
config_id=GuardrailConfigId.BIAS_CHECK,
|
|
20
|
+
api_key=config.get("api_key", ""),
|
|
21
|
+
reject_message=config.get("reject_message", "Bias detected"),
|
|
22
|
+
threshold=threshold,
|
|
20
23
|
)
|
|
21
24
|
return validated, "ok"
|
|
22
25
|
|
|
23
26
|
case "toxic_language":
|
|
24
27
|
threshold = float(config.get("threshold", 0.5))
|
|
25
28
|
validated = ToxicLanguageConfig(
|
|
26
|
-
config_id=GuardrailConfigId.TOXIC_LANGUAGE,
|
|
29
|
+
config_id=GuardrailConfigId.TOXIC_LANGUAGE,
|
|
30
|
+
api_key=config.get("api_key", ""),
|
|
31
|
+
reject_message=config.get(
|
|
32
|
+
"reject_message", "Toxic language detected"
|
|
33
|
+
),
|
|
34
|
+
threshold=threshold,
|
|
27
35
|
)
|
|
28
36
|
return validated, "ok"
|
|
29
37
|
|
|
@@ -35,6 +43,8 @@ def validate_guardrail(guardrail_id: str, config: dict) -> tuple[any, str]:
|
|
|
35
43
|
]
|
|
36
44
|
validated = CompetitionCheckConfig(
|
|
37
45
|
config_id=GuardrailConfigId.COMPETITION_CHECK,
|
|
46
|
+
api_key=config.get("api_key", ""),
|
|
47
|
+
reject_message=config.get("reject_message", "Competitor mentioned"),
|
|
38
48
|
competitors=competitors,
|
|
39
49
|
)
|
|
40
50
|
return validated, "ok"
|
|
@@ -73,4 +83,8 @@ def validate_guardrail(guardrail_id: str, config: dict) -> tuple[any, str]:
|
|
|
73
83
|
return None, f"Unknown guardrail type: {guardrail_id}"
|
|
74
84
|
|
|
75
85
|
except Exception as e:
|
|
76
|
-
|
|
86
|
+
error_msg = str(e)
|
|
87
|
+
if len(error_msg) > 100:
|
|
88
|
+
error_msg = error_msg[:100] + "..."
|
|
89
|
+
error_msg = error_msg.replace("<", "").replace(">", "")
|
|
90
|
+
return None, f"Validation error for {guardrail_id}: {error_msg}"
|
|
@@ -30,12 +30,15 @@ def validate_mcp_servers(
|
|
|
30
30
|
None,
|
|
31
31
|
f"Server '{name}': command is required for stdio transport",
|
|
32
32
|
)
|
|
33
|
-
elif transport in [
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
elif transport in [
|
|
34
|
+
"sse",
|
|
35
|
+
"streamable_http",
|
|
36
|
+
"websocket",
|
|
37
|
+
] and not server_data.get("url"):
|
|
38
|
+
return (
|
|
39
|
+
None,
|
|
40
|
+
f"Server '{name}': url is required for {transport} transport",
|
|
41
|
+
)
|
|
39
42
|
|
|
40
43
|
args = server_data.get("args", [])
|
|
41
44
|
if isinstance(args, str):
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
"""Widget components for the agent configuration screens."""
|
|
2
2
|
|
|
3
|
-
from .
|
|
4
|
-
from .observability_widget import ObservabilityWidget
|
|
3
|
+
from .chat_widget import ChatWidget
|
|
5
4
|
from .guardrails_widget import GuardrailsWidget
|
|
5
|
+
from .identity_widget import IdentityWidget
|
|
6
6
|
from .mcps_widget import MCPsWidget
|
|
7
|
+
from .memory_widget import MemoryWidget
|
|
8
|
+
from .observability_widget import ObservabilityWidget
|
|
7
9
|
from .serve_widget import ServeWidget
|
|
8
10
|
|
|
9
11
|
__all__ = [
|
|
10
|
-
"
|
|
11
|
-
"ObservabilityWidget",
|
|
12
|
+
"ChatWidget",
|
|
12
13
|
"GuardrailsWidget",
|
|
14
|
+
"IdentityWidget",
|
|
13
15
|
"MCPsWidget",
|
|
16
|
+
"MemoryWidget",
|
|
17
|
+
"ObservabilityWidget",
|
|
14
18
|
"ServeWidget",
|
|
15
19
|
]
|