@almadar/integrations 2.41.0 → 2.43.0
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/{BaseIntegration-CWdas-N_.d.ts → BaseIntegration-Dgwn1ZR6.d.ts} +5 -1
- package/dist/index.d.ts +40 -17
- package/dist/index.js +166 -3
- package/dist/index.js.map +1 -1
- package/dist/integrations/github/index.d.ts +1 -1
- package/dist/mocks/index.d.ts +1 -1
- package/dist/mocks/index.js +9 -1
- package/dist/mocks/index.js.map +1 -1
- package/dist/runtime/index.d.ts +3 -2
- package/dist/runtime/index.js +165 -2
- package/dist/runtime/index.js.map +1 -1
- package/dist/{store-DG5OGiuQ.d.ts → store-DP3gfEfJ.d.ts} +7 -101
- package/package.json +4 -4
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { g as IntegrationParams, I as IntegrationCallContext, d as IntegrationFactory } from '../BaseIntegration-
|
|
2
|
-
import { o as IntegrationName, m as IntegrationActionName, n as IntegrationContracts, e as CredentialStore } from '../store-
|
|
1
|
+
import { g as IntegrationParams, I as IntegrationCallContext, d as IntegrationFactory } from '../BaseIntegration-Dgwn1ZR6.js';
|
|
2
|
+
import { o as IntegrationName, m as IntegrationActionName, n as IntegrationContracts, e as CredentialStore } from '../store-DP3gfEfJ.js';
|
|
3
3
|
import '@almadar/core';
|
|
4
|
+
import '@almadar/llm';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Signature of the call-service handler returned by `createCallServiceHandler`
|
package/dist/runtime/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import webpush from 'web-push';
|
|
|
13
13
|
import { Readable } from 'stream';
|
|
14
14
|
import { getAvailableProvider, LLMClient, EmbeddingClient } from '@almadar/llm';
|
|
15
15
|
import { z } from 'zod';
|
|
16
|
+
import { findInputByAddress, payloadSchemaToJsonSchema, traceActivityPayload, EventPayloadSchema } from '@almadar/core';
|
|
16
17
|
import { execSync, spawn } from 'child_process';
|
|
17
18
|
import { tmpdir } from 'os';
|
|
18
19
|
import * as oidc from 'openid-client';
|
|
@@ -409,6 +410,8 @@ var IntegrationFactory = class {
|
|
|
409
410
|
constructor() {
|
|
410
411
|
this.instances = /* @__PURE__ */ new Map();
|
|
411
412
|
this.configs = /* @__PURE__ */ new Map();
|
|
413
|
+
/** Names whose app-wide instance was registered directly (mock infrastructure). */
|
|
414
|
+
this.registered = /* @__PURE__ */ new Set();
|
|
412
415
|
}
|
|
413
416
|
/**
|
|
414
417
|
* Configure an integration (doesn't instantiate yet). A `principal` scopes
|
|
@@ -423,7 +426,8 @@ var IntegrationFactory = class {
|
|
|
423
426
|
* back to the app-wide config when no per-principal config exists.
|
|
424
427
|
*/
|
|
425
428
|
get(name, principal) {
|
|
426
|
-
const
|
|
429
|
+
const registered = this.registered.has(name) && !this.configs.has(instanceKey(name, principal));
|
|
430
|
+
const key = registered ? name : instanceKey(name, principal);
|
|
427
431
|
const cached = this.instances.get(key);
|
|
428
432
|
if (cached) {
|
|
429
433
|
return cached;
|
|
@@ -467,6 +471,7 @@ var IntegrationFactory = class {
|
|
|
467
471
|
* Register an integration instance directly (used by mock infrastructure)
|
|
468
472
|
*/
|
|
469
473
|
registerInstance(name, instance, principal) {
|
|
474
|
+
if (principal === void 0) this.registered.add(name);
|
|
470
475
|
this.instances.set(instanceKey(name, principal), instance);
|
|
471
476
|
}
|
|
472
477
|
/**
|
|
@@ -477,8 +482,10 @@ var IntegrationFactory = class {
|
|
|
477
482
|
invalidate(name) {
|
|
478
483
|
if (name === void 0) {
|
|
479
484
|
this.instances.clear();
|
|
485
|
+
this.registered.clear();
|
|
480
486
|
return;
|
|
481
487
|
}
|
|
488
|
+
this.registered.delete(name);
|
|
482
489
|
for (const key of this.instances.keys()) {
|
|
483
490
|
if (key === name || key.startsWith(`${name}\0`)) {
|
|
484
491
|
this.instances.delete(key);
|
|
@@ -490,6 +497,7 @@ var IntegrationFactory = class {
|
|
|
490
497
|
*/
|
|
491
498
|
clear() {
|
|
492
499
|
this.instances.clear();
|
|
500
|
+
this.registered.clear();
|
|
493
501
|
}
|
|
494
502
|
/**
|
|
495
503
|
* Clear all instances and configs
|
|
@@ -497,6 +505,7 @@ var IntegrationFactory = class {
|
|
|
497
505
|
reset() {
|
|
498
506
|
this.instances.clear();
|
|
499
507
|
this.configs.clear();
|
|
508
|
+
this.registered.clear();
|
|
500
509
|
}
|
|
501
510
|
};
|
|
502
511
|
var installedStore = null;
|
|
@@ -1973,6 +1982,148 @@ var EsignIntegration = class extends BaseIntegration {
|
|
|
1973
1982
|
}
|
|
1974
1983
|
};
|
|
1975
1984
|
registerIntegration("esign", EsignIntegration);
|
|
1985
|
+
var DEFAULT_MAX_STEPS = 8;
|
|
1986
|
+
var TOOL_NAME = /^[a-zA-Z0-9_-]{1,64}$/;
|
|
1987
|
+
function toolName(raw) {
|
|
1988
|
+
if (!TOOL_NAME.test(raw)) {
|
|
1989
|
+
throw new Error(`call-tools: tool name '${raw}' is not a valid tool name (letters, digits, _ and -, at most 64)`);
|
|
1990
|
+
}
|
|
1991
|
+
return raw;
|
|
1992
|
+
}
|
|
1993
|
+
function buildTools(host, tools) {
|
|
1994
|
+
const inputs = host.inputs();
|
|
1995
|
+
const out = /* @__PURE__ */ new Map();
|
|
1996
|
+
for (const tool of tools) {
|
|
1997
|
+
if (tool.event !== void 0) {
|
|
1998
|
+
const input = findInputByAddress(inputs, tool.event, host.caller);
|
|
1999
|
+
if (!input) throw new Error(`call-tools: '${tool.event}' is not a declared external input`);
|
|
2000
|
+
const name = toolName(tool.event.split(".").join("__"));
|
|
2001
|
+
out.set(name, {
|
|
2002
|
+
def: {
|
|
2003
|
+
type: "function",
|
|
2004
|
+
function: { name, description: input.description ?? tool.event, parameters: payloadSchemaToJsonSchema(input.payloadSchema) }
|
|
2005
|
+
},
|
|
2006
|
+
target: { kind: "input", address: tool.event, input }
|
|
2007
|
+
});
|
|
2008
|
+
} else if (tool.read !== void 0) {
|
|
2009
|
+
const name = toolName(`read__${tool.read}`);
|
|
2010
|
+
out.set(name, {
|
|
2011
|
+
def: {
|
|
2012
|
+
type: "function",
|
|
2013
|
+
function: { name, description: `Read the ${tool.read} rows you may see`, parameters: { type: "object", properties: {} } }
|
|
2014
|
+
},
|
|
2015
|
+
target: { kind: "read", entity: tool.read }
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
return out;
|
|
2020
|
+
}
|
|
2021
|
+
function dispatchFailure(response) {
|
|
2022
|
+
if (response.rejections && response.rejections.length > 0) return response.rejections.map((r) => r.code).join(", ");
|
|
2023
|
+
if (response.guardFailed !== void 0) return `guard failed: ${response.guardFailed}`;
|
|
2024
|
+
if (!response.success) return response.error ?? "dispatch failed";
|
|
2025
|
+
const failed = (response.effectResults ?? []).find((r) => !r.success);
|
|
2026
|
+
if (failed) return failed.error ?? `${failed.effect} failed`;
|
|
2027
|
+
if (!response.transitioned) return "no transition";
|
|
2028
|
+
return void 0;
|
|
2029
|
+
}
|
|
2030
|
+
function parseArgs(args) {
|
|
2031
|
+
let value;
|
|
2032
|
+
try {
|
|
2033
|
+
value = JSON.parse(args === "" ? "{}" : args);
|
|
2034
|
+
} catch {
|
|
2035
|
+
value = void 0;
|
|
2036
|
+
}
|
|
2037
|
+
return EventPayloadSchema.safeParse(value);
|
|
2038
|
+
}
|
|
2039
|
+
async function runToolLoop(client, host, request, model, now = Date.now) {
|
|
2040
|
+
const tools = buildTools(host, request.tools);
|
|
2041
|
+
const defs = [...tools.values()].map((t) => t.def);
|
|
2042
|
+
const maxSteps = request.maxSteps ?? DEFAULT_MAX_STEPS;
|
|
2043
|
+
const conversation = [
|
|
2044
|
+
...request.instructions !== void 0 ? [{ role: "system", content: request.instructions }] : [],
|
|
2045
|
+
...request.messages.map((m) => ({ role: m.role, content: m.content }))
|
|
2046
|
+
];
|
|
2047
|
+
const userPrompt = [...request.messages].reverse().find((m) => m.role === "user")?.content ?? "";
|
|
2048
|
+
const steps = [];
|
|
2049
|
+
const record = (activity) => {
|
|
2050
|
+
steps.push(activity);
|
|
2051
|
+
host.message(traceActivityPayload(activity));
|
|
2052
|
+
};
|
|
2053
|
+
const usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0, modelCalls: 0 };
|
|
2054
|
+
while (usage.modelCalls < maxSteps) {
|
|
2055
|
+
const started = now();
|
|
2056
|
+
const turn = await client.callWithTools({ messages: conversation, tools: defs });
|
|
2057
|
+
const ended = now();
|
|
2058
|
+
usage.modelCalls += 1;
|
|
2059
|
+
if (turn.usage) {
|
|
2060
|
+
usage.promptTokens += turn.usage.promptTokens;
|
|
2061
|
+
usage.completionTokens += turn.usage.completionTokens;
|
|
2062
|
+
usage.totalTokens += turn.usage.totalTokens;
|
|
2063
|
+
}
|
|
2064
|
+
conversation.push(turn.message);
|
|
2065
|
+
const calls = turn.message.tool_calls ?? [];
|
|
2066
|
+
record({
|
|
2067
|
+
type: "llm_response",
|
|
2068
|
+
content: turn.message.content ?? "",
|
|
2069
|
+
llm: {
|
|
2070
|
+
service: "llm",
|
|
2071
|
+
provider: model.provider,
|
|
2072
|
+
model: model.model,
|
|
2073
|
+
systemPrompt: request.instructions ?? "",
|
|
2074
|
+
userPrompt,
|
|
2075
|
+
...calls.length > 0 ? { toolCallsJson: JSON.stringify(calls) } : {},
|
|
2076
|
+
durationMs: ended - started,
|
|
2077
|
+
...turn.usage ? { promptTokens: turn.usage.promptTokens, completionTokens: turn.usage.completionTokens } : {}
|
|
2078
|
+
},
|
|
2079
|
+
timestamp: ended
|
|
2080
|
+
});
|
|
2081
|
+
if (calls.length === 0) return { reply: turn.message.content ?? "", steps, usage };
|
|
2082
|
+
for (const call of calls) {
|
|
2083
|
+
const args = call.function.arguments;
|
|
2084
|
+
const tool = tools.get(call.function.name);
|
|
2085
|
+
const kind = tool === void 0 ? "invalid" : tool.target.kind;
|
|
2086
|
+
const label = tool === void 0 ? call.function.name : tool.target.kind === "input" ? tool.target.address : tool.target.entity;
|
|
2087
|
+
record({ type: "tool_call", tool: label, kind, argsText: args, timestamp: now() });
|
|
2088
|
+
let content;
|
|
2089
|
+
if (!tool) {
|
|
2090
|
+
content = JSON.stringify({ error: `'${call.function.name}' is not one of your tools` });
|
|
2091
|
+
record({ type: "tool_result", tool: label, kind, success: false, detail: "not an allowed tool", resultText: content, timestamp: now() });
|
|
2092
|
+
} else if (tool.target.kind === "read") {
|
|
2093
|
+
const rows = await host.read(tool.target.entity);
|
|
2094
|
+
content = JSON.stringify({ rows });
|
|
2095
|
+
record({ type: "tool_result", tool: label, kind, success: true, rows: rows.length, resultText: content, timestamp: now() });
|
|
2096
|
+
} else {
|
|
2097
|
+
const parsed = parseArgs(args);
|
|
2098
|
+
if (!parsed.success) {
|
|
2099
|
+
content = JSON.stringify({ error: "arguments must be a JSON object" });
|
|
2100
|
+
record({ type: "tool_result", tool: label, kind, success: false, detail: "arguments are not a JSON object", resultText: content, timestamp: now() });
|
|
2101
|
+
} else {
|
|
2102
|
+
const { input } = tool.target;
|
|
2103
|
+
const response = await host.dispatchInput(input.orbital, {
|
|
2104
|
+
targetTrait: input.trait,
|
|
2105
|
+
event: input.event,
|
|
2106
|
+
payload: parsed.data
|
|
2107
|
+
});
|
|
2108
|
+
const detail = dispatchFailure(response);
|
|
2109
|
+
content = JSON.stringify(response);
|
|
2110
|
+
record({ type: "tool_result", tool: label, kind, success: detail === void 0, ...detail !== void 0 ? { detail } : {}, resultText: content, timestamp: now() });
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
conversation.push({ role: "tool", content, tool_call_id: call.id });
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
throw new Error(`call-tools: no reply after ${maxSteps} model calls`);
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
// src/integrations/llm/index.ts
|
|
2120
|
+
var CallToolsParamsSchema = z.object({
|
|
2121
|
+
messages: z.array(z.object({ role: z.enum(["user", "assistant"]), content: z.string() })),
|
|
2122
|
+
tools: z.array(z.object({ event: z.string().optional(), read: z.string().optional() })),
|
|
2123
|
+
instructions: z.string().optional(),
|
|
2124
|
+
maxSteps: z.number().int().positive().optional(),
|
|
2125
|
+
model: z.string().optional()
|
|
2126
|
+
});
|
|
1976
2127
|
var LLMIntegration = class extends BaseIntegration {
|
|
1977
2128
|
constructor(config) {
|
|
1978
2129
|
super(config);
|
|
@@ -2013,7 +2164,7 @@ var LLMIntegration = class extends BaseIntegration {
|
|
|
2013
2164
|
}
|
|
2014
2165
|
return this.embeddings;
|
|
2015
2166
|
}
|
|
2016
|
-
async execute(action, params) {
|
|
2167
|
+
async execute(action, params, context) {
|
|
2017
2168
|
const validation = this.validateParams(action, params);
|
|
2018
2169
|
if (!validation.valid) return this.validationFailure(action, validation.errors);
|
|
2019
2170
|
const startTime = Date.now();
|
|
@@ -2035,6 +2186,10 @@ var LLMIntegration = class extends BaseIntegration {
|
|
|
2035
2186
|
case "embed":
|
|
2036
2187
|
data = await this.executeWithRetry(() => this.embed(params));
|
|
2037
2188
|
break;
|
|
2189
|
+
// Never retried: a retry would re-fire inputs the first attempt already fired.
|
|
2190
|
+
case "call-tools":
|
|
2191
|
+
data = await this.callTools(params, context);
|
|
2192
|
+
break;
|
|
2038
2193
|
default:
|
|
2039
2194
|
throw new UnknownActionError(this.config.name, action);
|
|
2040
2195
|
}
|
|
@@ -2125,6 +2280,14 @@ Return ONLY valid JSON matching the schema.`,
|
|
|
2125
2280
|
});
|
|
2126
2281
|
return result;
|
|
2127
2282
|
}
|
|
2283
|
+
async callTools(params, context) {
|
|
2284
|
+
if (!context?.host) {
|
|
2285
|
+
throw new Error("llm.call-tools needs host ports: run it from a host that lends the running app (call-service on a server)");
|
|
2286
|
+
}
|
|
2287
|
+
const request = CallToolsParamsSchema.parse(params);
|
|
2288
|
+
const client = request.model !== void 0 ? new LLMClient({ provider: this.provider, model: request.model }) : this.getClient();
|
|
2289
|
+
return runToolLoop(client, context.host, request, { provider: client.getProvider(), model: client.getModelName() });
|
|
2290
|
+
}
|
|
2128
2291
|
async embed(params) {
|
|
2129
2292
|
const { texts } = params;
|
|
2130
2293
|
if (!Array.isArray(texts)) {
|