agentram 0.1.20 → 0.1.21
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.
- package/agentram/cli.py +53 -6
- package/package.json +1 -1
- package/pyproject.toml +1 -1
package/agentram/cli.py
CHANGED
|
@@ -5,6 +5,7 @@ import asyncio
|
|
|
5
5
|
from concurrent.futures import ThreadPoolExecutor
|
|
6
6
|
import json
|
|
7
7
|
import os
|
|
8
|
+
import re
|
|
8
9
|
import shlex
|
|
9
10
|
import shutil
|
|
10
11
|
import textwrap
|
|
@@ -16,6 +17,51 @@ from .config import default_ram_root
|
|
|
16
17
|
from .mcp_server import McpContext, call_tool
|
|
17
18
|
from .orchestration import AgentModelEndpoint, MultiModelCodingOrchestrator
|
|
18
19
|
|
|
20
|
+
|
|
21
|
+
ENV_NAME_PATTERN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
|
22
|
+
RAW_KEY_PREFIXES = ("sk-", "sk_", "sk-proj-", "gsk_", "xai-", "AIza")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def looks_like_raw_api_key(value: str) -> bool:
|
|
26
|
+
stripped = value.strip()
|
|
27
|
+
if not stripped:
|
|
28
|
+
return False
|
|
29
|
+
if stripped.startswith(RAW_KEY_PREFIXES):
|
|
30
|
+
return True
|
|
31
|
+
return not bool(ENV_NAME_PATTERN.fullmatch(stripped))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def update_env_file_value(path: Path, key: str, value: str) -> None:
|
|
35
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
36
|
+
lines = path.read_text(encoding="utf-8").splitlines() if path.exists() else []
|
|
37
|
+
assignment = f"{key}={value}"
|
|
38
|
+
updated = False
|
|
39
|
+
output: list[str] = []
|
|
40
|
+
for line in lines:
|
|
41
|
+
if line.strip().startswith(f"{key}="):
|
|
42
|
+
output.append(assignment)
|
|
43
|
+
updated = True
|
|
44
|
+
else:
|
|
45
|
+
output.append(line)
|
|
46
|
+
if not updated:
|
|
47
|
+
if output and output[-1].strip():
|
|
48
|
+
output.append("")
|
|
49
|
+
output.append("# Added by AgentRAM TUI setup")
|
|
50
|
+
output.append(assignment)
|
|
51
|
+
path.write_text("\n".join(output) + "\n", encoding="utf-8")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def resolve_api_key_env(slot: str, value: str) -> tuple[str, str | None]:
|
|
55
|
+
stripped = value.strip()
|
|
56
|
+
if not stripped:
|
|
57
|
+
return "", None
|
|
58
|
+
if looks_like_raw_api_key(stripped):
|
|
59
|
+
env_name = f"AGENTRAM_{slot.upper()}_API_KEY"
|
|
60
|
+
update_env_file_value(Path(".env"), env_name, stripped)
|
|
61
|
+
os.environ[env_name] = stripped
|
|
62
|
+
return env_name, env_name
|
|
63
|
+
return stripped, None
|
|
64
|
+
|
|
19
65
|
SUBAGENTS: dict[str, str] = {
|
|
20
66
|
"memory": "Summarize important facts, decisions, files, risks, and next tasks into AgentRAM events. Do not edit code.",
|
|
21
67
|
"planner": "Break the coding request into small ordered steps. Check AgentRAM goal drift before scope changes.",
|
|
@@ -335,7 +381,7 @@ def run_textual_tui(context: McpContext) -> bool:
|
|
|
335
381
|
yield Input(placeholder="Custom provider if preset=Custom", id=f"setup-{slot}-custom-provider", classes="setup-input")
|
|
336
382
|
yield Input(value=default_model, placeholder="Model name", id=f"setup-{slot}-model", classes="setup-input")
|
|
337
383
|
yield Input(placeholder="Base URL, e.g. http://localhost:20128/v1", id=f"setup-{slot}-base-url", classes="setup-input")
|
|
338
|
-
yield Input(value="OPENAI_API_KEY", placeholder="API key env name
|
|
384
|
+
yield Input(value="OPENAI_API_KEY", placeholder="API key or env name", id=f"setup-{slot}-api-key-env", classes="setup-input")
|
|
339
385
|
with Horizontal(id="setup-actions"):
|
|
340
386
|
yield Button("Save setup", id="setup-save", variant="primary")
|
|
341
387
|
yield Button("Skip", id="setup-skip")
|
|
@@ -396,10 +442,8 @@ def run_textual_tui(context: McpContext) -> bool:
|
|
|
396
442
|
provider = self.setup_provider(slot)
|
|
397
443
|
model = self.setup_value(f"setup-{slot}-model")
|
|
398
444
|
base_url = self.setup_value(f"setup-{slot}-base-url")
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
self.chat.write(f"agentram > setup error: {slot} api key env must be variable name, not raw key")
|
|
402
|
-
return
|
|
445
|
+
api_key_input = self.setup_value(f"setup-{slot}-api-key-env")
|
|
446
|
+
api_key_env, saved_env_name = resolve_api_key_env(slot, api_key_input)
|
|
403
447
|
if not model:
|
|
404
448
|
continue
|
|
405
449
|
endpoint = AgentModelEndpoint(
|
|
@@ -411,7 +455,10 @@ def run_textual_tui(context: McpContext) -> bool:
|
|
|
411
455
|
modalities=["text"],
|
|
412
456
|
)
|
|
413
457
|
self.context.profile_store.set_agent(slot, endpoint)
|
|
414
|
-
|
|
458
|
+
if saved_env_name:
|
|
459
|
+
bound.append(f"{slot}={provider}:{model} key=.env:{saved_env_name}")
|
|
460
|
+
else:
|
|
461
|
+
bound.append(f"{slot}={provider}:{model}")
|
|
415
462
|
if not bound:
|
|
416
463
|
self.chat.write("agentram > setup skipped: no model entered")
|
|
417
464
|
else:
|
package/package.json
CHANGED