@melaya/runner 1.0.85 → 1.0.87
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/dist/assistantHost.py +75 -2
- package/dist/connection.js +7 -1
- package/package.json +42 -42
package/dist/assistantHost.py
CHANGED
|
@@ -46,6 +46,55 @@ _stream = {"turnId": "", "lens": {}, "cancel": False}
|
|
|
46
46
|
_CANCELLED = object()
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
class _RedactingStdout:
|
|
50
|
+
"""Wrap stdout to strip long base64 runs before they reach the log.
|
|
51
|
+
|
|
52
|
+
agentscope prints every agent message verbatim (the `MelayaAssistant: {...}`
|
|
53
|
+
lines), which for phone_screenshot tool results embeds a full base64 JPEG —
|
|
54
|
+
tens of KB per turn that floods and explodes the runner logs. This filter is
|
|
55
|
+
line-buffered and only replaces base64-looking runs (>=200 chars), so the
|
|
56
|
+
structured `MELASSIST ` event lines (short text deltas / tool names) pass
|
|
57
|
+
through byte-for-byte and the runner's line parser is unaffected."""
|
|
58
|
+
|
|
59
|
+
import re as _re
|
|
60
|
+
_B64 = _re.compile(r"[A-Za-z0-9+/]{200,}={0,2}")
|
|
61
|
+
|
|
62
|
+
def __init__(self, real):
|
|
63
|
+
self._real = real
|
|
64
|
+
self._buf = ""
|
|
65
|
+
|
|
66
|
+
def _scrub(self, s: str) -> str:
|
|
67
|
+
return self._B64.sub(lambda m: f"<redacted base64 {len(m.group(0))} bytes>", s)
|
|
68
|
+
|
|
69
|
+
def write(self, s):
|
|
70
|
+
try:
|
|
71
|
+
self._buf += s
|
|
72
|
+
while "\n" in self._buf:
|
|
73
|
+
line, self._buf = self._buf.split("\n", 1)
|
|
74
|
+
self._real.write(self._scrub(line) + "\n")
|
|
75
|
+
return len(s)
|
|
76
|
+
except Exception:
|
|
77
|
+
return self._real.write(s)
|
|
78
|
+
|
|
79
|
+
def flush(self):
|
|
80
|
+
try:
|
|
81
|
+
if self._buf:
|
|
82
|
+
self._real.write(self._scrub(self._buf))
|
|
83
|
+
self._buf = ""
|
|
84
|
+
self._real.flush()
|
|
85
|
+
except Exception:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
def __getattr__(self, k):
|
|
89
|
+
return getattr(self._real, k)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# Install the redactor as early as possible so agentscope's message printing is
|
|
93
|
+
# filtered for the whole process lifetime.
|
|
94
|
+
if not isinstance(sys.stdout, _RedactingStdout):
|
|
95
|
+
sys.stdout = _RedactingStdout(sys.stdout)
|
|
96
|
+
|
|
97
|
+
|
|
49
98
|
def _emit(turn_id: str, kind: str, **fields) -> None:
|
|
50
99
|
"""Write one structured event line to stdout (flushed) for the runner to relay."""
|
|
51
100
|
try:
|
|
@@ -113,10 +162,25 @@ def _build_agent():
|
|
|
113
162
|
# mobile only. These POST to /api/v1/private/assistant-tool + /phone/command
|
|
114
163
|
# with MELAYA_API_KEY; tenant scope is enforced server-side.
|
|
115
164
|
categories = ["melaya_agent"] + (["phone"] if phone_enabled else [])
|
|
165
|
+
# Connector tool sets the user enabled for THIS chat (any service — odoo,
|
|
166
|
+
# stripe, shopify, …). When present, use a LAZY toolkit: base tools stay
|
|
167
|
+
# active+pinned, and the connectors' tools are deferred + discoverable via
|
|
168
|
+
# search_tools/activate_tool (≤25 active) so even a 178-tool connector never
|
|
169
|
+
# explodes context. Bounded to the selected services so the model can't reach
|
|
170
|
+
# a connector the user didn't enable / has no creds for.
|
|
171
|
+
connector_services = [s.strip().lower() for s in os.environ.get("MEL_ASSISTANT_CONNECTORS", "").split(",") if s.strip()]
|
|
116
172
|
try:
|
|
117
|
-
|
|
173
|
+
if connector_services:
|
|
174
|
+
from shared.orchestration.lazy_registry import build_lazy_toolkit
|
|
175
|
+
toolkit = build_lazy_toolkit(
|
|
176
|
+
active_categories=categories,
|
|
177
|
+
include_categories=categories + connector_services,
|
|
178
|
+
budget=int(os.environ.get("MEL_LAZY_BUDGET", "25")),
|
|
179
|
+
)
|
|
180
|
+
else:
|
|
181
|
+
toolkit = build_toolkit(categories=categories)
|
|
118
182
|
except Exception as exc:
|
|
119
|
-
_log(f"
|
|
183
|
+
_log(f"toolkit build failed (connectors={connector_services}): {exc}; retrying melaya_agent only")
|
|
120
184
|
try:
|
|
121
185
|
toolkit = build_toolkit(categories=["melaya_agent"])
|
|
122
186
|
except Exception:
|
|
@@ -145,6 +209,14 @@ def _build_agent():
|
|
|
145
209
|
"blocked; only then report what you did and what (if anything) is blocked.\n"
|
|
146
210
|
if phone_enabled else ""
|
|
147
211
|
)
|
|
212
|
+
connector_rule = (
|
|
213
|
+
"- You also have CONNECTOR tools the user enabled (" + ", ".join(connector_services) + "). "
|
|
214
|
+
"Their tools are not all loaded upfront — call search_tools(query=...) to find the "
|
|
215
|
+
"right one for the user's question, then activate_tool(name=...) to load it, then call "
|
|
216
|
+
"it. Prefer specific tools (e.g. odoo_sale_order_list) over generic ones. Never invent "
|
|
217
|
+
"data — read it from the connector.\n"
|
|
218
|
+
if connector_services else ""
|
|
219
|
+
)
|
|
148
220
|
sys_prompt = (
|
|
149
221
|
"You are the Melaya Assistant, the in-app copilot of the Melaya "
|
|
150
222
|
"agent-orchestration platform. You have READ-ONLY tools over the data "
|
|
@@ -156,6 +228,7 @@ def _build_agent():
|
|
|
156
228
|
"templates or evals — never invent numbers. For cost, melaya_cost_summary "
|
|
157
229
|
"supports dimension='pipeline' to find which pipeline cost the most.\n"
|
|
158
230
|
+ phone_rule
|
|
231
|
+
+ connector_rule
|
|
159
232
|
+ "- Be concise and concrete; format small tables or bullet lists when comparing items.\n"
|
|
160
233
|
"- If a question is outside the platform, answer normally without tools.\n"
|
|
161
234
|
+ (f"- Answer in the user's language: {language}.\n" if language and language != "en" else "")
|
package/dist/connection.js
CHANGED
|
@@ -893,7 +893,13 @@ export async function connect(opts) {
|
|
|
893
893
|
MEL_ASSISTANT_MODEL: String(payload.model || ""),
|
|
894
894
|
MEL_ASSISTANT_LANGUAGE: String(payload.language || "en"),
|
|
895
895
|
MEL_ASSISTANT_SURFACE: String(payload.surface || ""),
|
|
896
|
-
//
|
|
896
|
+
// Connector tool sets the user enabled for this chat (comma-joined service
|
|
897
|
+
// ids). The host seeds a lazy toolkit from these so ANY connector's tools
|
|
898
|
+
// are reachable without exploding context.
|
|
899
|
+
MEL_ASSISTANT_CONNECTORS: Array.isArray(payload.connectors) ? payload.connectors.join(",") : "",
|
|
900
|
+
// Gate write connector-tools behind the in-chat approval card (fail-safe).
|
|
901
|
+
MEL_ASSISTANT_CONNECTOR_HITL: Array.isArray(payload.connectors) && payload.connectors.length ? "1" : "",
|
|
902
|
+
// Per-user creds (incl. MELAYA_API_KEY + the selected connectors' env).
|
|
897
903
|
...(payload.credentials || {}),
|
|
898
904
|
};
|
|
899
905
|
const proc = spawn(envResult.pythonPath, ["-u", stagedHost], { cwd: workDir, env, stdio: ["pipe", "pipe", "pipe"] });
|
package/package.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@melaya/runner",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
|
|
5
|
-
"license": "UNLICENSED",
|
|
6
|
-
"private": false,
|
|
7
|
-
"type": "module",
|
|
8
|
-
"bin": {
|
|
9
|
-
"melaya-runner": "dist/cli.js"
|
|
10
|
-
},
|
|
11
|
-
"main": "dist/index.js",
|
|
12
|
-
"files": [
|
|
13
|
-
"dist/**/*.js",
|
|
14
|
-
"dist/**/*.d.ts",
|
|
15
|
-
"dist/**/*.py",
|
|
16
|
-
"localRagIngest.py",
|
|
17
|
-
"localRagRetrieve.py",
|
|
18
|
-
"nltk_data/**",
|
|
19
|
-
"README.md"
|
|
20
|
-
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "tsc && node -e \"const fs=require('fs'); fs.copyFileSync('localRagIngest.py','dist/localRagIngest.py'); fs.copyFileSync('localRagRetrieve.py','dist/localRagRetrieve.py'); fs.copyFileSync('src/assistantHost.py','dist/assistantHost.py')\"",
|
|
23
|
-
"prepublishOnly": "npm run build"
|
|
24
|
-
},
|
|
25
|
-
"dependencies": {
|
|
26
|
-
"chalk": "^5.3.0",
|
|
27
|
-
"commander": "^12.0.0",
|
|
28
|
-
"ora": "^8.0.0",
|
|
29
|
-
"playwright": "^1.47.0",
|
|
30
|
-
"socket.io-client": "^4.8.0"
|
|
31
|
-
},
|
|
32
|
-
"devDependencies": {
|
|
33
|
-
"@types/node": "^20.0.0",
|
|
34
|
-
"typescript": "^5.5.0"
|
|
35
|
-
},
|
|
36
|
-
"engines": {
|
|
37
|
-
"node": ">=18"
|
|
38
|
-
},
|
|
39
|
-
"publishConfig": {
|
|
40
|
-
"access": "public"
|
|
41
|
-
}
|
|
42
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@melaya/runner",
|
|
3
|
+
"version": "1.0.87",
|
|
4
|
+
"description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"private": false,
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"melaya-runner": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/**/*.js",
|
|
14
|
+
"dist/**/*.d.ts",
|
|
15
|
+
"dist/**/*.py",
|
|
16
|
+
"localRagIngest.py",
|
|
17
|
+
"localRagRetrieve.py",
|
|
18
|
+
"nltk_data/**",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc && node -e \"const fs=require('fs'); fs.copyFileSync('localRagIngest.py','dist/localRagIngest.py'); fs.copyFileSync('localRagRetrieve.py','dist/localRagRetrieve.py'); fs.copyFileSync('src/assistantHost.py','dist/assistantHost.py')\"",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"chalk": "^5.3.0",
|
|
27
|
+
"commander": "^12.0.0",
|
|
28
|
+
"ora": "^8.0.0",
|
|
29
|
+
"playwright": "^1.47.0",
|
|
30
|
+
"socket.io-client": "^4.8.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"typescript": "^5.5.0"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
}
|
|
42
|
+
}
|