@fonoster/autopilot 0.27.0 → 0.31.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/README.md +15 -52
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -5
- package/dist/knowledge/AbstractKnowledgeBase.d.ts +3 -2
- package/dist/knowledge/AbstractKnowledgeBase.js +2 -7
- package/dist/knowledge/FilesKnowledgeBase.d.ts +1 -1
- package/dist/knowledge/MeteredEmbeddings.d.ts +47 -0
- package/dist/knowledge/MeteredEmbeddings.js +58 -0
- package/dist/knowledge/types.d.ts +1 -1
- package/dist/models/LanguageModelFactory.d.ts +5 -24
- package/dist/models/LanguageModelFactory.js +0 -1
- package/dist/models/createLanguageModel.d.ts +2 -2
- package/dist/models/createLanguageModel.js +2 -2
- package/dist/models/evaluations/createEvalEffectiveConfig.d.ts +10 -106
- package/dist/models/evaluations/createEvalEffectiveConfig.js +47 -7
- package/dist/models/evaluations/createEvaluateIntelligence.d.ts +21 -20
- package/dist/models/evaluations/createEvaluateIntelligence.js +53 -23
- package/dist/models/evaluations/createTestTextSimilarity.d.ts +7 -1
- package/dist/models/evaluations/createTestTextSimilarity.js +19 -1
- package/dist/models/evaluations/evalTestCases.d.ts +2 -2
- package/dist/models/evaluations/evalTestCases.js +7 -7
- package/dist/models/evaluations/evaluateScenario.js +2 -2
- package/dist/models/evaluations/evaluateStep.d.ts +1 -1
- package/dist/models/evaluations/evaluateStep.js +2 -2
- package/dist/models/evaluations/extractAiResponse.d.ts +2 -3
- package/dist/models/evaluations/extractAiResponse.js +3 -3
- package/dist/models/evaluations/index.d.ts +1 -0
- package/dist/models/evaluations/index.js +1 -0
- package/dist/models/evaluations/runEval.d.ts +2 -20
- package/dist/models/evaluations/runEval.js +7 -7
- package/dist/models/evaluations/session/EvalSession.d.ts +73 -0
- package/dist/models/evaluations/session/EvalSession.js +134 -0
- package/dist/models/evaluations/session/evaluateSessionScenario.d.ts +58 -0
- package/dist/models/evaluations/session/evaluateSessionScenario.js +182 -0
- package/dist/{vad → models/evaluations/session}/index.d.ts +3 -2
- package/dist/{vad → models/evaluations/session}/index.js +3 -2
- package/dist/models/evaluations/session/runSessionEval.d.ts +20 -0
- package/dist/models/evaluations/session/runSessionEval.js +59 -0
- package/dist/models/evaluations/textSimilaryPrompt.d.ts +1 -1
- package/dist/models/evaluations/textSimilaryPrompt.js +1 -1
- package/dist/models/evaluations/types.d.ts +7 -6
- package/dist/models/types.d.ts +19 -1
- package/dist/session/Agent.d.ts +173 -0
- package/dist/session/Agent.js +548 -0
- package/dist/{loadAssistantFromAPI.d.ts → session/AsyncQueue.d.ts} +12 -7
- package/dist/session/AsyncQueue.js +67 -0
- package/dist/{machine/guards/index.d.ts → session/config.d.ts} +2 -2
- package/dist/session/config.js +27 -0
- package/dist/session/createAutopilot.d.ts +48 -0
- package/dist/session/createAutopilot.js +151 -0
- package/dist/session/hosted.d.ts +454 -0
- package/dist/session/hosted.js +501 -0
- package/dist/{assistants → session}/index.d.ts +11 -2
- package/dist/{machine/actors → session}/index.js +11 -3
- package/dist/{machine/actions/appendSpeech.js → session/knowledge.d.ts} +14 -17
- package/dist/session/knowledge.js +10 -0
- package/dist/session/llm.d.ts +113 -0
- package/dist/session/llm.js +127 -0
- package/dist/session/machine.d.ts +206 -0
- package/dist/session/machine.js +126 -0
- package/dist/session/main.js +45 -0
- package/dist/session/meteredThink.d.ts +45 -0
- package/dist/session/meteredThink.js +125 -0
- package/dist/session/sentences.d.ts +5 -0
- package/dist/session/sentences.js +53 -0
- package/dist/session/telemetry.d.ts +34 -0
- package/dist/session/telemetry.js +55 -0
- package/dist/{machine/actors/index.d.ts → session/think.d.ts} +2 -3
- package/dist/{machine/actions/announceSystemError.js → session/think.js} +8 -10
- package/dist/session/tools.d.ts +22 -0
- package/dist/session/tools.js +83 -0
- package/dist/tools/builtin/transferToolDefinition.js +1 -1
- package/dist/voice/index.d.ts +0 -1
- package/dist/voice/index.js +0 -1
- package/dist/voice/types.d.ts +5 -18
- package/package.json +15 -16
- package/dist/Autopilot.d.ts +0 -14
- package/dist/Autopilot.js +0 -133
- package/dist/assistants/index.js +0 -36
- package/dist/assistants/loadAndValidateAssistant.d.ts +0 -3
- package/dist/assistants/loadAndValidateAssistant.js +0 -49
- package/dist/assistants/types.d.ts +0 -24
- package/dist/assistants/types.js +0 -2
- package/dist/envs.d.ts +0 -15
- package/dist/envs.js +0 -78
- package/dist/handleVoiceRequest.d.ts +0 -3
- package/dist/handleVoiceRequest.js +0 -167
- package/dist/loadAssistantConfigFromFile.d.ts +0 -3
- package/dist/loadAssistantConfigFromFile.js +0 -68
- package/dist/loadAssistantFromAPI.js +0 -98
- package/dist/machine/actions/announceSystemError.d.ts +0 -4
- package/dist/machine/actions/appendSpeech.d.ts +0 -1
- package/dist/machine/actions/cleanSpeech.d.ts +0 -1
- package/dist/machine/actions/cleanSpeech.js +0 -23
- package/dist/machine/actions/goodbye.d.ts +0 -4
- package/dist/machine/actions/goodbye.js +0 -31
- package/dist/machine/actions/increaseIdleTimeoutCount.d.ts +0 -1
- package/dist/machine/actions/increaseIdleTimeoutCount.js +0 -31
- package/dist/machine/actions/index.d.ts +0 -26
- package/dist/machine/actions/index.js +0 -37
- package/dist/machine/actions/interruptPlayback.d.ts +0 -4
- package/dist/machine/actions/interruptPlayback.js +0 -43
- package/dist/machine/actions/resetIdleTimeoutCount.d.ts +0 -1
- package/dist/machine/actions/resetIdleTimeoutCount.js +0 -31
- package/dist/machine/actions/resetState.d.ts +0 -1
- package/dist/machine/actions/resetState.js +0 -32
- package/dist/machine/actors/doAnnounceIdleTimeout.d.ts +0 -4
- package/dist/machine/actors/doAnnounceIdleTimeout.js +0 -41
- package/dist/machine/actors/doGreetUser.d.ts +0 -4
- package/dist/machine/actors/doGreetUser.js +0 -45
- package/dist/machine/actors/doProcessUserRequest.d.ts +0 -4
- package/dist/machine/actors/doProcessUserRequest.js +0 -75
- package/dist/machine/context.d.ts +0 -52
- package/dist/machine/context.js +0 -28
- package/dist/machine/delays.d.ts +0 -30
- package/dist/machine/delays.js +0 -37
- package/dist/machine/guards/hasSpeechResult.d.ts +0 -4
- package/dist/machine/guards/hasSpeechResult.js +0 -30
- package/dist/machine/guards/idleTimeoutCountExceedsMax.d.ts +0 -4
- package/dist/machine/guards/idleTimeoutCountExceedsMax.js +0 -31
- package/dist/machine/guards/index.js +0 -25
- package/dist/machine/index.d.ts +0 -19
- package/dist/machine/index.js +0 -23
- package/dist/machine/machine.d.ts +0 -408
- package/dist/machine/machine.js +0 -206
- package/dist/machine/setup.d.ts +0 -328
- package/dist/machine/setup.js +0 -116
- package/dist/machine/types.d.ts +0 -54
- package/dist/machine/types.js +0 -2
- package/dist/sendConversationEndedEvent.d.ts +0 -32
- package/dist/sendConversationEndedEvent.js +0 -66
- package/dist/sendConversationStartedEvent.d.ts +0 -6
- package/dist/sendConversationStartedEvent.js +0 -63
- package/dist/server.d.ts +0 -2
- package/dist/server.js +0 -25
- package/dist/types.d.ts +0 -40
- package/dist/types.js +0 -8
- package/dist/vad/SileroVad.d.ts +0 -18
- package/dist/vad/SileroVad.js +0 -40
- package/dist/vad/SileroVadModel.d.ts +0 -14
- package/dist/vad/SileroVadModel.js +0 -83
- package/dist/vad/chunkToFloat32Array.d.ts +0 -20
- package/dist/vad/chunkToFloat32Array.js +0 -34
- package/dist/vad/createVad.d.ts +0 -3
- package/dist/vad/createVad.js +0 -126
- package/dist/vad/types.d.ts +0 -60
- package/dist/vad/types.js +0 -2
- package/dist/vadWorker.js +0 -36
- package/dist/voice/Voice.d.ts +0 -61
- package/dist/voice/Voice.js +0 -96
- package/dist/voiceServerSetup.d.ts +0 -2
- package/dist/voiceServerSetup.js +0 -29
- /package/dist/{vadWorker.d.ts → session/main.d.ts} +0 -0
|
@@ -19,48 +19,78 @@ exports.createEvaluateIntelligence = createEvaluateIntelligence;
|
|
|
19
19
|
* See the License for the specific language governing permissions and
|
|
20
20
|
* limitations under the License.
|
|
21
21
|
*/
|
|
22
|
+
const crypto_1 = require("crypto");
|
|
22
23
|
const common_1 = require("@fonoster/common");
|
|
23
24
|
const logger_1 = require("@fonoster/logger");
|
|
24
25
|
const pb_util_1 = require("pb-util");
|
|
25
|
-
const
|
|
26
|
+
const meteredThink_1 = require("../../session/meteredThink");
|
|
26
27
|
const createEvalEffectiveConfig_1 = require("./createEvalEffectiveConfig");
|
|
27
28
|
const runEval_1 = require("./runEval");
|
|
29
|
+
const session_1 = require("./session");
|
|
28
30
|
const stepReportToEventPayload_1 = require("./stepReportToEventPayload");
|
|
29
31
|
const logger = (0, logger_1.getLogger)({ service: "apiserver", filePath: __filename });
|
|
30
|
-
|
|
32
|
+
const keyOf = (k) => typeof k === "string" ? { key: k, source: "platform" } : k;
|
|
33
|
+
/**
|
|
34
|
+
* Runs an agent's test cases (rework-autopilot 8.2: the agent is a session agent; its
|
|
35
|
+
* catalog id picks the provider, and keys come from `keys`, never from the agent).
|
|
36
|
+
*/
|
|
37
|
+
function createEvaluateIntelligence(keys, reportUsage) {
|
|
31
38
|
const evaluateIntelligence = async (call) => {
|
|
32
39
|
const { request } = call;
|
|
33
40
|
const { intelligence } = request;
|
|
34
41
|
const accessKeyId = (0, common_1.getAccessKeyIdFromCall)(call);
|
|
35
|
-
logger.verbose("call to evaluateIntelligence", {
|
|
36
|
-
accessKeyId,
|
|
37
|
-
llmProductRef: intelligence.productRef,
|
|
38
|
-
evalLlmProductRef: "llm.openai"
|
|
39
|
-
});
|
|
40
42
|
try {
|
|
41
43
|
const config = pb_util_1.struct.decode(intelligence.config);
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
.parse({
|
|
48
|
-
productRef: intelligence.productRef,
|
|
49
|
-
config
|
|
44
|
+
const agent = common_1.sessionAgentSchema.parse(config);
|
|
45
|
+
const provider = agent.languageModel.model.split("/")[0];
|
|
46
|
+
logger.verbose("call to evaluateIntelligence", {
|
|
47
|
+
accessKeyId,
|
|
48
|
+
model: agent.languageModel.model
|
|
50
49
|
});
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
50
|
+
const agentKey = keyOf(await keys(provider, accessKeyId));
|
|
51
|
+
const judgeKey = keyOf(await keys("openai", accessKeyId));
|
|
52
|
+
const apiKey = agentKey?.key;
|
|
53
|
+
const evaluationApiKey = judgeKey?.key;
|
|
54
|
+
// Eval tokens are metered with origin eval, one scope per request of this run.
|
|
55
|
+
const runId = (0, crypto_1.randomUUID)();
|
|
56
|
+
let request = 0;
|
|
57
|
+
const sink = {
|
|
58
|
+
usage: (lines) => reportUsage?.(accessKeyId, lines, `eval|${runId}|${request++}`),
|
|
59
|
+
missing: (catalogId) => logger.warn(`eval: ${catalogId} reported no usage`, { accessKeyId })
|
|
60
|
+
};
|
|
61
|
+
if (!apiKey && provider !== "ollama")
|
|
62
|
+
throw new Error(`no ${provider} key for this workspace or platform`);
|
|
63
|
+
if (!evaluationApiKey)
|
|
64
|
+
throw new Error("no openai key for the evaluation model (workspace or platform)");
|
|
65
|
+
const callbacks = {
|
|
55
66
|
onStepResult: (scenarioRef, stepReport) => {
|
|
56
|
-
|
|
57
|
-
call.write(payload);
|
|
67
|
+
call.write((0, stepReportToEventPayload_1.stepReportToEventPayload)(scenarioRef, stepReport));
|
|
58
68
|
},
|
|
59
69
|
onScenarioComplete: (scenarioRef, overallPassed) => {
|
|
60
|
-
|
|
61
|
-
call.write(payload);
|
|
70
|
+
call.write((0, stepReportToEventPayload_1.scenarioSummaryToEventPayload)(scenarioRef, overallPassed));
|
|
62
71
|
}
|
|
72
|
+
};
|
|
73
|
+
// The session engine (streaming replies, structured tool messages) for OpenAI-compatible
|
|
74
|
+
// providers; the LangChain path stays for the rest until their adapters land (EV-B1).
|
|
75
|
+
const sessionThink = (0, session_1.sessionThinkFor)(agent, apiKey);
|
|
76
|
+
if (sessionThink) {
|
|
77
|
+
const think = (0, meteredThink_1.meteredThink)(sessionThink, {
|
|
78
|
+
catalogId: agent.languageModel.model,
|
|
79
|
+
keySource: agentKey?.source ?? "platform"
|
|
80
|
+
}, sink);
|
|
81
|
+
await (0, session_1.runSessionEval)({
|
|
82
|
+
agent,
|
|
83
|
+
think,
|
|
84
|
+
evaluationApiKey,
|
|
85
|
+
judgeUsage: { sink, keySource: judgeKey?.source ?? "platform" }
|
|
86
|
+
}, callbacks);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const effectiveConfig = (0, createEvalEffectiveConfig_1.createEvalEffectiveConfig)(agent, {
|
|
90
|
+
apiKey,
|
|
91
|
+
evaluationApiKey
|
|
63
92
|
});
|
|
93
|
+
await (0, runEval_1.runEval)({ intelligence: { config: effectiveConfig } }, callbacks);
|
|
64
94
|
}
|
|
65
95
|
catch (error) {
|
|
66
96
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import { MeteredModel, UsageSink } from "../../session/meteredThink";
|
|
1
2
|
export declare function createTestTextSimilarity(evalsLanguageModel: {
|
|
2
3
|
provider: any;
|
|
3
4
|
model: string;
|
|
4
5
|
baseUrl?: string;
|
|
5
6
|
apiKey?: string;
|
|
6
|
-
}, systemPrompt: string
|
|
7
|
+
}, systemPrompt: string,
|
|
8
|
+
/** Meters the judge's tokens (add-usage-metering); the judge is always OpenAI. */
|
|
9
|
+
usage?: {
|
|
10
|
+
sink: UsageSink;
|
|
11
|
+
keySource: MeteredModel["keySource"];
|
|
12
|
+
}): (text1: string, text2: string) => Promise<boolean>;
|
|
@@ -21,7 +21,10 @@ exports.createTestTextSimilarity = createTestTextSimilarity;
|
|
|
21
21
|
*/
|
|
22
22
|
const messages_1 = require("@langchain/core/messages");
|
|
23
23
|
const openai_1 = require("@langchain/openai");
|
|
24
|
-
|
|
24
|
+
const meteredThink_1 = require("../../session/meteredThink");
|
|
25
|
+
function createTestTextSimilarity(evalsLanguageModel, systemPrompt,
|
|
26
|
+
/** Meters the judge's tokens (add-usage-metering); the judge is always OpenAI. */
|
|
27
|
+
usage) {
|
|
25
28
|
if (!evalsLanguageModel.apiKey) {
|
|
26
29
|
throw new Error("API key is required for text similarity evaluation.");
|
|
27
30
|
}
|
|
@@ -37,6 +40,21 @@ function createTestTextSimilarity(evalsLanguageModel, systemPrompt) {
|
|
|
37
40
|
new messages_1.HumanMessage(`Text 1: ${text1}\nText 2: ${text2}`)
|
|
38
41
|
];
|
|
39
42
|
const response = await llm.invoke(messages);
|
|
43
|
+
if (usage) {
|
|
44
|
+
const model = {
|
|
45
|
+
catalogId: `openai/${evalsLanguageModel.model}`,
|
|
46
|
+
keySource: usage.keySource
|
|
47
|
+
};
|
|
48
|
+
const m = response.usage_metadata;
|
|
49
|
+
if (m)
|
|
50
|
+
usage.sink.usage((0, meteredThink_1.usageLines)(model, {
|
|
51
|
+
inputTokens: m.input_tokens,
|
|
52
|
+
outputTokens: m.output_tokens,
|
|
53
|
+
cachedTokens: m.input_token_details?.cache_read ?? 0
|
|
54
|
+
}));
|
|
55
|
+
else
|
|
56
|
+
usage.sink.missing(model.catalogId);
|
|
57
|
+
}
|
|
40
58
|
const reply = response.content?.toString().trim().toLowerCase();
|
|
41
59
|
return reply === "true" || reply === "yes";
|
|
42
60
|
};
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { ScenarioEvaluationReport } from "@fonoster/types";
|
|
20
|
-
import {
|
|
20
|
+
import { EvalAgent } from "../types";
|
|
21
21
|
export declare function evalTestCases(autopilotApplication: {
|
|
22
22
|
intelligence: {
|
|
23
|
-
config:
|
|
23
|
+
config: EvalAgent;
|
|
24
24
|
};
|
|
25
25
|
}): Promise<ScenarioEvaluationReport[]>;
|
|
@@ -7,7 +7,7 @@ const evaluateScenario_1 = require("./evaluateScenario");
|
|
|
7
7
|
const textSimilaryPrompt_1 = require("./textSimilaryPrompt");
|
|
8
8
|
async function evalTestCases(autopilotApplication) {
|
|
9
9
|
const { testCases } = autopilotApplication.intelligence.config;
|
|
10
|
-
const { config:
|
|
10
|
+
const { config: agentConfig } = autopilotApplication.intelligence;
|
|
11
11
|
const voice = {
|
|
12
12
|
say: async (_) => { }
|
|
13
13
|
};
|
|
@@ -17,7 +17,7 @@ async function evalTestCases(autopilotApplication) {
|
|
|
17
17
|
const scenario = scenarios[i];
|
|
18
18
|
const languageModel = (0, createLanguageModel_1.createLanguageModel)({
|
|
19
19
|
voice,
|
|
20
|
-
|
|
20
|
+
agentConfig: autopilotApplication.intelligence.config,
|
|
21
21
|
knowledgeBase: {
|
|
22
22
|
load: async () => { },
|
|
23
23
|
queryKnowledgeBase: async (query) => query
|
|
@@ -25,12 +25,12 @@ async function evalTestCases(autopilotApplication) {
|
|
|
25
25
|
telephonyContext: scenario.telephonyContext
|
|
26
26
|
});
|
|
27
27
|
const testTextSimilarity = (0, createTestTextSimilarity_1.createTestTextSimilarity)({
|
|
28
|
-
provider:
|
|
29
|
-
model:
|
|
30
|
-
apiKey:
|
|
31
|
-
},
|
|
28
|
+
provider: agentConfig.testCases?.evalsLanguageModel?.provider,
|
|
29
|
+
model: agentConfig.testCases?.evalsLanguageModel?.model ?? "",
|
|
30
|
+
apiKey: agentConfig.testCases?.evalsLanguageModel?.apiKey
|
|
31
|
+
}, agentConfig.testCases?.evalsSystemPrompt || textSimilaryPrompt_1.textSimilaryPrompt);
|
|
32
32
|
const evaluationReport = await (0, evaluateScenario_1.evaluateScenario)({
|
|
33
|
-
|
|
33
|
+
agentConfig,
|
|
34
34
|
scenario,
|
|
35
35
|
languageModel,
|
|
36
36
|
testTextSimilarity
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.evaluateScenario = evaluateScenario;
|
|
4
4
|
const evaluateStep_1 = require("./evaluateStep");
|
|
5
5
|
async function evaluateScenario(config, onStepResult) {
|
|
6
|
-
const { scenario, languageModel, testTextSimilarity,
|
|
6
|
+
const { scenario, languageModel, testTextSimilarity, agentConfig } = config;
|
|
7
7
|
const results = [];
|
|
8
8
|
const steps = scenario.conversation;
|
|
9
9
|
for (let i = 0; i < steps.length; i++) {
|
|
@@ -12,7 +12,7 @@ async function evaluateScenario(config, onStepResult) {
|
|
|
12
12
|
step,
|
|
13
13
|
languageModel,
|
|
14
14
|
testTextSimilarity,
|
|
15
|
-
|
|
15
|
+
agentConfig
|
|
16
16
|
});
|
|
17
17
|
results.push(stepResult);
|
|
18
18
|
if (onStepResult)
|
|
@@ -18,4 +18,4 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { StepEvaluationReport } from "@fonoster/types";
|
|
20
20
|
import { EvaluateStepParams } from "./types";
|
|
21
|
-
export declare function evaluateStep({ step, languageModel, testTextSimilarity,
|
|
21
|
+
export declare function evaluateStep({ step, languageModel, testTextSimilarity, agentConfig }: EvaluateStepParams): Promise<StepEvaluationReport>;
|
|
@@ -4,7 +4,7 @@ exports.evaluateStep = evaluateStep;
|
|
|
4
4
|
const evaluateTextResponse_1 = require("./evaluateTextResponse");
|
|
5
5
|
const evaluateToolCalls_1 = require("./evaluateToolCalls");
|
|
6
6
|
const extractAiResponse_1 = require("./extractAiResponse");
|
|
7
|
-
async function evaluateStep({ step, languageModel, testTextSimilarity,
|
|
7
|
+
async function evaluateStep({ step, languageModel, testTextSimilarity, agentConfig }) {
|
|
8
8
|
const stepResult = {
|
|
9
9
|
humanInput: step.userInput,
|
|
10
10
|
expectedResponse: step.expected.text.response,
|
|
@@ -14,7 +14,7 @@ async function evaluateStep({ step, languageModel, testTextSimilarity, assistant
|
|
|
14
14
|
};
|
|
15
15
|
try {
|
|
16
16
|
const response = await languageModel.invoke(step.userInput);
|
|
17
|
-
stepResult.aiResponse = (0, extractAiResponse_1.extractAiResponse)(response,
|
|
17
|
+
stepResult.aiResponse = (0, extractAiResponse_1.extractAiResponse)(response, agentConfig);
|
|
18
18
|
const textEvaluation = await (0, evaluateTextResponse_1.evaluateTextResponse)({
|
|
19
19
|
type: step.expected.text.type,
|
|
20
20
|
response: step.expected.text.response
|
|
@@ -16,10 +16,9 @@
|
|
|
16
16
|
* See the License for the specific language governing permissions and
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
|
-
import {
|
|
20
|
-
import { InvocationResult } from "../types";
|
|
19
|
+
import { EvalAgent, InvocationResult } from "../types";
|
|
21
20
|
/**
|
|
22
21
|
* Maps language model invocation result to the text that should be compared
|
|
23
22
|
* in eval (hangup/transfer use config messages).
|
|
24
23
|
*/
|
|
25
|
-
export declare function extractAiResponse(response: InvocationResult,
|
|
24
|
+
export declare function extractAiResponse(response: InvocationResult, agentConfig: EvalAgent): string;
|
|
@@ -5,14 +5,14 @@ exports.extractAiResponse = extractAiResponse;
|
|
|
5
5
|
* Maps language model invocation result to the text that should be compared
|
|
6
6
|
* in eval (hangup/transfer use config messages).
|
|
7
7
|
*/
|
|
8
|
-
function extractAiResponse(response,
|
|
8
|
+
function extractAiResponse(response, agentConfig) {
|
|
9
9
|
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
10
10
|
const topTool = response.toolCalls[0];
|
|
11
11
|
if (topTool.name === "hangup") {
|
|
12
|
-
return
|
|
12
|
+
return agentConfig.conversationSettings?.goodbyeMessage ?? "";
|
|
13
13
|
}
|
|
14
14
|
if (topTool.name === "transfer") {
|
|
15
|
-
return
|
|
15
|
+
return agentConfig.conversationSettings?.transferOptions?.message ?? "";
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
return response.content ?? "";
|
|
@@ -1,26 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
-
* http://github.com/fonoster/fonoster
|
|
4
|
-
*
|
|
5
|
-
* This file is part of Fonoster
|
|
6
|
-
*
|
|
7
|
-
* Licensed under the MIT License (the "License");
|
|
8
|
-
* you may not use this file except in compliance with
|
|
9
|
-
* the License. You may obtain a copy of the License at
|
|
10
|
-
*
|
|
11
|
-
* https://opensource.org/licenses/MIT
|
|
12
|
-
*
|
|
13
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
-
* See the License for the specific language governing permissions and
|
|
17
|
-
* limitations under the License.
|
|
18
|
-
*/
|
|
19
|
-
import { AssistantConfig } from "../../assistants";
|
|
1
|
+
import { EvalAgent } from "../types";
|
|
20
2
|
import { RunEvalCallbacks } from "./types";
|
|
21
3
|
export type RunEvalConfig = {
|
|
22
4
|
intelligence: {
|
|
23
|
-
config:
|
|
5
|
+
config: EvalAgent;
|
|
24
6
|
};
|
|
25
7
|
};
|
|
26
8
|
export declare function runEval(autopilotApplication: RunEvalConfig, callbacks: RunEvalCallbacks): Promise<void>;
|
|
@@ -7,7 +7,7 @@ const evaluateScenario_1 = require("./evaluateScenario");
|
|
|
7
7
|
const textSimilaryPrompt_1 = require("./textSimilaryPrompt");
|
|
8
8
|
async function runEval(autopilotApplication, callbacks) {
|
|
9
9
|
const { testCases } = autopilotApplication.intelligence.config;
|
|
10
|
-
const
|
|
10
|
+
const agentConfig = autopilotApplication.intelligence.config;
|
|
11
11
|
const voice = {
|
|
12
12
|
say: async (_) => { }
|
|
13
13
|
};
|
|
@@ -15,7 +15,7 @@ async function runEval(autopilotApplication, callbacks) {
|
|
|
15
15
|
for (const scenario of scenarios) {
|
|
16
16
|
const languageModel = (0, createLanguageModel_1.createLanguageModel)({
|
|
17
17
|
voice,
|
|
18
|
-
|
|
18
|
+
agentConfig,
|
|
19
19
|
knowledgeBase: {
|
|
20
20
|
load: async () => { },
|
|
21
21
|
queryKnowledgeBase: async (query) => query
|
|
@@ -23,12 +23,12 @@ async function runEval(autopilotApplication, callbacks) {
|
|
|
23
23
|
telephonyContext: scenario.telephonyContext
|
|
24
24
|
});
|
|
25
25
|
const testTextSimilarity = (0, createTestTextSimilarity_1.createTestTextSimilarity)({
|
|
26
|
-
provider:
|
|
27
|
-
model:
|
|
28
|
-
apiKey:
|
|
29
|
-
},
|
|
26
|
+
provider: agentConfig.testCases?.evalsLanguageModel?.provider,
|
|
27
|
+
model: agentConfig.testCases?.evalsLanguageModel?.model ?? "",
|
|
28
|
+
apiKey: agentConfig.testCases?.evalsLanguageModel?.apiKey
|
|
29
|
+
}, agentConfig.testCases?.evalsSystemPrompt ?? textSimilaryPrompt_1.textSimilaryPrompt);
|
|
30
30
|
const report = await (0, evaluateScenario_1.evaluateScenario)({
|
|
31
|
-
|
|
31
|
+
agentConfig,
|
|
32
32
|
scenario,
|
|
33
33
|
languageModel,
|
|
34
34
|
testTextSimilarity
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** What the engine did on the session, in order. */
|
|
2
|
+
export type EvalOutput = {
|
|
3
|
+
kind: "say";
|
|
4
|
+
text: string;
|
|
5
|
+
/** True when the engine streamed the reply (say with an async iterable). */
|
|
6
|
+
streamed: boolean;
|
|
7
|
+
/** The pieces as they arrived (sentences, for a streamed reply). */
|
|
8
|
+
pieces: string[];
|
|
9
|
+
} | {
|
|
10
|
+
kind: "play";
|
|
11
|
+
url: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "dtmf";
|
|
14
|
+
digits: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "hangup";
|
|
17
|
+
} | {
|
|
18
|
+
kind: "transfer";
|
|
19
|
+
to: string;
|
|
20
|
+
};
|
|
21
|
+
export type EvalTelephony = {
|
|
22
|
+
callDirection?: string;
|
|
23
|
+
ingressNumber?: string;
|
|
24
|
+
callerNumber?: string;
|
|
25
|
+
metadata?: Record<string, string>;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* An in-memory session for conversation evals (rework-autopilot 1.4): the session engine runs on
|
|
29
|
+
* it exactly as on a call, but there is no AppBridge, socket or audio. Turns go in as `turn.ended`;
|
|
30
|
+
* every reply is "heard" in full (no barge-in in an eval).
|
|
31
|
+
*/
|
|
32
|
+
export declare class EvalSession {
|
|
33
|
+
readonly outputs: EvalOutput[];
|
|
34
|
+
readonly sessionRef = "eval-session";
|
|
35
|
+
readonly callRef = "eval-call";
|
|
36
|
+
readonly appRef = "eval-app";
|
|
37
|
+
readonly direction: string;
|
|
38
|
+
readonly from: string;
|
|
39
|
+
readonly to: string;
|
|
40
|
+
readonly metadata: Record<string, string>;
|
|
41
|
+
private readonly events;
|
|
42
|
+
private pending;
|
|
43
|
+
private endedReason?;
|
|
44
|
+
constructor(telephony?: EvalTelephony);
|
|
45
|
+
get ended(): boolean;
|
|
46
|
+
/** Says in flight (a streamed reply is in flight until its last piece). */
|
|
47
|
+
get busy(): boolean;
|
|
48
|
+
on(event: string, listener: (payload: never) => void): this;
|
|
49
|
+
/** Observe outputs as they are recorded (the evaluator waits on these). */
|
|
50
|
+
onOutput(listener: (output: EvalOutput) => void): void;
|
|
51
|
+
/** The caller finished a turn. */
|
|
52
|
+
turn(text: string): void;
|
|
53
|
+
/** The call is over (end of scenario, or the engine hung up or transferred). */
|
|
54
|
+
end(reason?: string): void;
|
|
55
|
+
say(input: string | AsyncIterable<string>, _options?: unknown): Promise<{
|
|
56
|
+
interrupted: boolean;
|
|
57
|
+
heardText: string;
|
|
58
|
+
heardMs: number;
|
|
59
|
+
}>;
|
|
60
|
+
stopSay(): Promise<void>;
|
|
61
|
+
play(url: string, _options?: unknown): Promise<{
|
|
62
|
+
interrupted: boolean;
|
|
63
|
+
heardMs: number;
|
|
64
|
+
}>;
|
|
65
|
+
playDtmf(digits: string): Promise<void>;
|
|
66
|
+
subscribe(_events: string[]): Promise<void>;
|
|
67
|
+
listen(_options: unknown): Promise<void>;
|
|
68
|
+
note(_data: Record<string, unknown>): Promise<void>;
|
|
69
|
+
hangup(): Promise<void>;
|
|
70
|
+
transfer(to: string): Promise<void>;
|
|
71
|
+
private record;
|
|
72
|
+
private assertOpen;
|
|
73
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EvalSession = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
6
|
+
* http://github.com/fonoster/fonoster
|
|
7
|
+
*
|
|
8
|
+
* This file is part of Fonoster
|
|
9
|
+
*
|
|
10
|
+
* Licensed under the MIT License (the "License");
|
|
11
|
+
* you may not use this file except in compliance with
|
|
12
|
+
* the License. You may obtain a copy of the License at
|
|
13
|
+
*
|
|
14
|
+
* https://opensource.org/licenses/MIT
|
|
15
|
+
*
|
|
16
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
17
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
18
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
19
|
+
* See the License for the specific language governing permissions and
|
|
20
|
+
* limitations under the License.
|
|
21
|
+
*/
|
|
22
|
+
const events_1 = require("events");
|
|
23
|
+
const voice_1 = require("@fonoster/voice");
|
|
24
|
+
/**
|
|
25
|
+
* An in-memory session for conversation evals (rework-autopilot 1.4): the session engine runs on
|
|
26
|
+
* it exactly as on a call, but there is no AppBridge, socket or audio. Turns go in as `turn.ended`;
|
|
27
|
+
* every reply is "heard" in full (no barge-in in an eval).
|
|
28
|
+
*/
|
|
29
|
+
class EvalSession {
|
|
30
|
+
constructor(telephony = {}) {
|
|
31
|
+
this.outputs = [];
|
|
32
|
+
this.sessionRef = "eval-session";
|
|
33
|
+
this.callRef = "eval-call";
|
|
34
|
+
this.appRef = "eval-app";
|
|
35
|
+
this.events = new events_1.EventEmitter();
|
|
36
|
+
this.pending = 0;
|
|
37
|
+
this.direction = (telephony.callDirection ?? "FROM_PSTN").startsWith("TO")
|
|
38
|
+
? "outbound"
|
|
39
|
+
: "inbound";
|
|
40
|
+
this.from = telephony.callerNumber ?? "";
|
|
41
|
+
this.to = telephony.ingressNumber ?? "";
|
|
42
|
+
this.metadata = telephony.metadata ?? {};
|
|
43
|
+
this.events.setMaxListeners(50);
|
|
44
|
+
}
|
|
45
|
+
get ended() {
|
|
46
|
+
return this.endedReason !== undefined;
|
|
47
|
+
}
|
|
48
|
+
/** Says in flight (a streamed reply is in flight until its last piece). */
|
|
49
|
+
get busy() {
|
|
50
|
+
return this.pending > 0;
|
|
51
|
+
}
|
|
52
|
+
on(event, listener) {
|
|
53
|
+
this.events.on(event, listener);
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
/** Observe outputs as they are recorded (the evaluator waits on these). */
|
|
57
|
+
onOutput(listener) {
|
|
58
|
+
this.events.on("__output", listener);
|
|
59
|
+
}
|
|
60
|
+
/** The caller finished a turn. */
|
|
61
|
+
turn(text) {
|
|
62
|
+
this.events.emit("turn.ended", {
|
|
63
|
+
type: "turn.ended",
|
|
64
|
+
text,
|
|
65
|
+
language: "en",
|
|
66
|
+
confidence: 1,
|
|
67
|
+
durationMs: 0
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** The call is over (end of scenario, or the engine hung up or transferred). */
|
|
71
|
+
end(reason = "eval_done") {
|
|
72
|
+
if (this.ended)
|
|
73
|
+
return;
|
|
74
|
+
this.endedReason = reason;
|
|
75
|
+
this.events.emit("end", { type: "session.end", reason });
|
|
76
|
+
}
|
|
77
|
+
// ---- the Session surface the engine uses ---------------------------------------------
|
|
78
|
+
async say(input, _options = {}) {
|
|
79
|
+
this.assertOpen();
|
|
80
|
+
this.pending++;
|
|
81
|
+
try {
|
|
82
|
+
const pieces = [];
|
|
83
|
+
if (typeof input === "string")
|
|
84
|
+
pieces.push(input);
|
|
85
|
+
else
|
|
86
|
+
for await (const piece of input)
|
|
87
|
+
pieces.push(piece);
|
|
88
|
+
const text = pieces.join("");
|
|
89
|
+
this.record({
|
|
90
|
+
kind: "say",
|
|
91
|
+
text,
|
|
92
|
+
streamed: typeof input !== "string",
|
|
93
|
+
pieces
|
|
94
|
+
});
|
|
95
|
+
return { interrupted: false, heardText: text, heardMs: 0 };
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
this.pending--;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async stopSay() { }
|
|
102
|
+
async play(url, _options = {}) {
|
|
103
|
+
this.assertOpen();
|
|
104
|
+
this.record({ kind: "play", url });
|
|
105
|
+
return { interrupted: false, heardMs: 0 };
|
|
106
|
+
}
|
|
107
|
+
async playDtmf(digits) {
|
|
108
|
+
this.assertOpen();
|
|
109
|
+
this.record({ kind: "dtmf", digits });
|
|
110
|
+
}
|
|
111
|
+
async subscribe(_events) { }
|
|
112
|
+
async listen(_options) { }
|
|
113
|
+
async note(_data) { }
|
|
114
|
+
async hangup() {
|
|
115
|
+
if (this.ended)
|
|
116
|
+
return;
|
|
117
|
+
this.record({ kind: "hangup" });
|
|
118
|
+
this.end("hangup");
|
|
119
|
+
}
|
|
120
|
+
async transfer(to) {
|
|
121
|
+
this.assertOpen();
|
|
122
|
+
this.record({ kind: "transfer", to });
|
|
123
|
+
this.end("transferred");
|
|
124
|
+
}
|
|
125
|
+
record(output) {
|
|
126
|
+
this.outputs.push(output);
|
|
127
|
+
this.events.emit("__output", output);
|
|
128
|
+
}
|
|
129
|
+
assertOpen() {
|
|
130
|
+
if (this.endedReason)
|
|
131
|
+
throw new voice_1.SessionEndedError(this.endedReason);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.EvalSession = EvalSession;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { ScenarioEvaluationReport, StepEvaluationReport } from "@fonoster/types";
|
|
20
|
+
import { SessionAgent } from "../../../session/config";
|
|
21
|
+
import { ChatMessage, Think } from "../../../session/think";
|
|
22
|
+
import { ToolPort } from "../../../session/tools";
|
|
23
|
+
import { EvalScenario } from "../types";
|
|
24
|
+
/** A step report with what the session engine streamed (extra fields, ignored on the wire). */
|
|
25
|
+
export type SessionStepReport = StepEvaluationReport & {
|
|
26
|
+
streaming: {
|
|
27
|
+
/** The pieces of the reply as the engine sent them to `say` (sentences). */
|
|
28
|
+
pieces: string[];
|
|
29
|
+
firstTokenMs?: number;
|
|
30
|
+
firstSpeakMs?: number;
|
|
31
|
+
};
|
|
32
|
+
/** Tool calls and results as stored in the history (structured tool messages). */
|
|
33
|
+
toolMessages: ChatMessage[];
|
|
34
|
+
};
|
|
35
|
+
export type SessionScenarioReport = ScenarioEvaluationReport & {
|
|
36
|
+
steps: SessionStepReport[];
|
|
37
|
+
};
|
|
38
|
+
export type SessionEvalOptions = {
|
|
39
|
+
agent: SessionAgent;
|
|
40
|
+
/** The language model the engine streams from (a registry adapter, or the fake LLM). */
|
|
41
|
+
think: Think;
|
|
42
|
+
testTextSimilarity: (text1: string, text2: string) => Promise<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* Runs the agent's own tools. Defaults to `evalToolPort`: evals never call tool endpoints
|
|
45
|
+
* (an eval must not book an appointment), every call succeeds with `{ status: "ok" }`.
|
|
46
|
+
*/
|
|
47
|
+
tools?: ToolPort;
|
|
48
|
+
/** Upper bound for one step (default 60 s). */
|
|
49
|
+
stepTimeoutMs?: number;
|
|
50
|
+
};
|
|
51
|
+
/** The default eval tool port: records nothing, calls nothing, every tool succeeds. */
|
|
52
|
+
export declare const evalToolPort: ToolPort;
|
|
53
|
+
/**
|
|
54
|
+
* Runs one test-case scenario through the session engine (rework-autopilot 1.4): the real state
|
|
55
|
+
* machine and Agent on an in-memory session, replies streamed sentence by sentence, tool calls
|
|
56
|
+
* read back from the structured tool messages in the history.
|
|
57
|
+
*/
|
|
58
|
+
export declare function evaluateSessionScenario(options: SessionEvalOptions, scenario: EvalScenario, onStepResult?: (scenarioRef: string, report: StepEvaluationReport) => void | Promise<void>): Promise<SessionScenarioReport>;
|