- Replace Hermes pi module integration with pi-ai session streaming and updated runtime adapter contracts - Remove legacy engine guard scaffolding and add hermes-stream-client coverage for streaming behavior - Rewrite plugin and engine e2e tests to align with the new runtime flow and regenerate dist artifacts - Update Hermes runtime README and package metadata to document pi-ai execution expectations
85 lines
3.3 KiB
JavaScript
85 lines
3.3 KiB
JavaScript
import { randomUUID } from "node:crypto";
|
|
import { getModel, streamSimple, } from "@mariozechner/pi-ai";
|
|
const DEFAULT_PROVIDER = "anthropic";
|
|
const DEFAULT_MODEL_ID = "claude-sonnet-4-5";
|
|
function resolveStringSetting(value) {
|
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
}
|
|
export function resolveModelConfig(settings) {
|
|
const provider = resolveStringSetting(settings?.provider) ?? resolveStringSetting(process.env.HERMES_PROVIDER) ?? DEFAULT_PROVIDER;
|
|
const modelId = resolveStringSetting(settings?.modelId) ?? resolveStringSetting(process.env.HERMES_MODEL_ID) ?? DEFAULT_MODEL_ID;
|
|
const apiKey = resolveStringSetting(settings?.apiKey) ?? resolveStringSetting(process.env.HERMES_API_KEY);
|
|
const thinkingLevel = resolveStringSetting(settings?.thinkingLevel) ?? resolveStringSetting(process.env.HERMES_THINKING_LEVEL) ?? undefined;
|
|
return { provider, modelId, apiKey, thinkingLevel };
|
|
}
|
|
export function createStreamSession(options) {
|
|
const model = getModel(options.provider, options.modelId);
|
|
return {
|
|
model,
|
|
systemPrompt: options.systemPrompt,
|
|
messages: [],
|
|
apiKey: options.apiKey,
|
|
thinkingLevel: options.thinkingLevel,
|
|
sessionId: randomUUID(),
|
|
lastModelDescription: `${model.provider}/${model.id}`,
|
|
callbacks: options.callbacks ?? {},
|
|
usage: undefined,
|
|
dispose: () => undefined,
|
|
};
|
|
}
|
|
export async function streamPrompt(session, _userMessage) {
|
|
const context = {
|
|
systemPrompt: session.systemPrompt,
|
|
messages: [...session.messages],
|
|
};
|
|
const options = {
|
|
sessionId: session.sessionId,
|
|
};
|
|
if (session.apiKey) {
|
|
options.apiKey = session.apiKey;
|
|
}
|
|
if (session.thinkingLevel) {
|
|
options.reasoning = session.thinkingLevel;
|
|
}
|
|
const stream = streamSimple(session.model, context, options);
|
|
let fullText = "";
|
|
for await (const event of stream) {
|
|
handleStreamEvent(event, session, (delta) => {
|
|
fullText += delta;
|
|
});
|
|
}
|
|
const finalMessage = await stream.result();
|
|
const responseText = finalMessage.content
|
|
.filter((content) => content.type === "text")
|
|
.map((content) => content.text)
|
|
.join("") || fullText;
|
|
session.messages.push({ role: "assistant", content: responseText });
|
|
session.lastModelDescription = `${session.model.provider}/${session.model.id}`;
|
|
}
|
|
function handleStreamEvent(event, session, onTextDelta) {
|
|
if (event.type === "text_delta") {
|
|
session.callbacks.onText?.(event.delta);
|
|
onTextDelta(event.delta);
|
|
return;
|
|
}
|
|
if (event.type === "thinking_delta") {
|
|
session.callbacks.onThinking?.(event.delta);
|
|
return;
|
|
}
|
|
if (event.type === "toolcall_end") {
|
|
session.callbacks.onToolStart?.(event.toolCall.name, event.toolCall.arguments);
|
|
session.callbacks.onToolEnd?.(event.toolCall.name, false, event.toolCall.arguments);
|
|
return;
|
|
}
|
|
if (event.type === "error") {
|
|
const errorMessage = event.error.errorMessage ?? "Hermes stream failed";
|
|
throw new Error(errorMessage);
|
|
}
|
|
if (event.type === "done") {
|
|
session.usage = event.message.usage;
|
|
}
|
|
}
|
|
export function describeStreamModel(session) {
|
|
return session.lastModelDescription;
|
|
}
|
|
//# sourceMappingURL=pi-module.js.map
|