fix: use native Pi identity and refresh provider catalogs
This commit is contained in:
+9
-1
@@ -11,6 +11,14 @@ import { builtinProviders } from "@earendil-works/pi-ai/providers/all";
|
||||
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
||||
import { type ModelRole } from "../../config.ts";
|
||||
import { DEFAULT_THINKING_ORDER } from "../core/settings.ts";
|
||||
import packageMetadata from "../../package.json" with { type: "json" };
|
||||
|
||||
/** App identity is immutable and shared by Models and every native Agent.
|
||||
* Agent's default Pi attribution otherwise overrides the Models identity. */
|
||||
export const APP_IDENTITY = Object.freeze({
|
||||
userAgent: Object.freeze({ custom: `learnharness/${packageMetadata.version}` }),
|
||||
attribution: Object.freeze({ name: packageMetadata.name, url: packageMetadata.homepage }),
|
||||
});
|
||||
|
||||
/**
|
||||
* The one engine factory every agent's model comes from (the pi-ai swap,
|
||||
@@ -47,7 +55,7 @@ let collection: ReturnType<typeof createModels> | undefined;
|
||||
* keyPresent. */
|
||||
export function piModels() {
|
||||
if (!collection) {
|
||||
collection = createModels();
|
||||
collection = createModels({ identity: APP_IDENTITY });
|
||||
for (const provider of builtinProviders()) collection.setProvider(provider);
|
||||
}
|
||||
return collection;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type { AssistantMessage, Message, ToolResultMessage, Usage, Tool } from "@earendil-works/pi-ai";
|
||||
import { normalizeContext } from "@earendil-works/pi-ai";
|
||||
import { Type } from "typebox";
|
||||
@@ -183,6 +184,7 @@ export async function runPdfReaderAgent(
|
||||
// BYOK (owner, 2026-09-22): the level rides the injected engine — no
|
||||
// factory pin to consult (engine.level is the resolved/clamped value).
|
||||
const reasoning = engine.level;
|
||||
const sessionId = randomUUID();
|
||||
const prompt = [
|
||||
`PDF path: ${pdfPath}`,
|
||||
`Question (what the teacher needs from this document): ${input.question || "(no question — give a structural overview)"}`,
|
||||
@@ -207,6 +209,7 @@ export async function runPdfReaderAgent(
|
||||
INSTRUCTIONS,
|
||||
messages,
|
||||
toolDeclarations,
|
||||
sessionId,
|
||||
reasoning,
|
||||
opts.abortSignal,
|
||||
opts.chatId ? providerEventObserver(opts.chatId, "job:pdfReader") : undefined,
|
||||
@@ -395,6 +398,7 @@ async function streamOnce(
|
||||
systemPrompt: string,
|
||||
messages: Message[],
|
||||
tools: Tool[],
|
||||
sessionId: string,
|
||||
reasoning: ThinkingLevel | undefined,
|
||||
abortSignal?: AbortSignal,
|
||||
onProviderStreamEvent?: ProviderEventObserver,
|
||||
@@ -403,6 +407,7 @@ async function streamOnce(
|
||||
// and tool declarations fold into a leading system message.
|
||||
// normalizeContext is the library's only Context→TranscriptContext entry.
|
||||
const stream = engine.streamFn(engine.model, normalizeContext({ systemPrompt, messages, tools }), {
|
||||
sessionId,
|
||||
reasoning: reasoning && reasoning !== "off" ? reasoning : undefined,
|
||||
signal: abortSignal,
|
||||
onProviderStreamEvent: onProviderStreamEvent as never,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Agent, type AgentEvent, type AgentMessage, type AgentTool } from "@earendil-works/pi-agent-core";
|
||||
import type { AssistantMessage, ToolResultMessage, Usage } from "@earendil-works/pi-ai";
|
||||
import { piEngineFor, type PiEngine } from "../models.ts";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { APP_IDENTITY, piEngineFor, type PiEngine } from "../models.ts";
|
||||
import { providerEventObserver } from "../provider-events.ts";
|
||||
import type { ModelRole } from "../../../config.ts";
|
||||
|
||||
@@ -64,6 +65,9 @@ export async function runSubagent(tools: AgentTool<any, any>[], opts: SubagentRu
|
||||
// Debug recorder (owner, 2026-10-04): raw provider events per job run.
|
||||
const providerEvents = opts.chatId ? providerEventObserver(opts.chatId, `job:${opts.role}`) : undefined;
|
||||
const agent = new Agent({
|
||||
identity: APP_IDENTITY,
|
||||
// One unsaved conversation per invocation, stable through every turn.
|
||||
sessionId: randomUUID(),
|
||||
initialState: {
|
||||
systemPrompt: opts.systemPrompt,
|
||||
model: engine.model,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type { AssistantMessage, Message, Tool, ToolResultMessage, Usage } from "@earendil-works/pi-ai";
|
||||
import { normalizeContext } from "@earendil-works/pi-ai";
|
||||
import { Type } from "typebox";
|
||||
@@ -142,6 +143,7 @@ export async function runVisualMaker(input: VisualMakerInput, opts: VisualMakerO
|
||||
// BYOK (owner, 2026-09-22): the level rides the injected engine — no
|
||||
// factory pin to consult (engine.level is the resolved/clamped value).
|
||||
const reasoning = engine.level;
|
||||
const sessionId = randomUUID();
|
||||
const prompt = [
|
||||
"Brief for ONE precise figure (from the teacher, verbatim — it is everything you know):",
|
||||
brief,
|
||||
@@ -164,6 +166,7 @@ export async function runVisualMaker(input: VisualMakerInput, opts: VisualMakerO
|
||||
INSTRUCTIONS,
|
||||
messages,
|
||||
toolDeclarations,
|
||||
sessionId,
|
||||
reasoning,
|
||||
opts.abortSignal,
|
||||
opts.chatId ? providerEventObserver(opts.chatId, "job:visualMaker") : undefined,
|
||||
@@ -317,6 +320,7 @@ async function streamOnce(
|
||||
systemPrompt: string,
|
||||
messages: Message[],
|
||||
tools: Tool[],
|
||||
sessionId: string,
|
||||
reasoning: ThinkingLevel | undefined,
|
||||
abortSignal?: AbortSignal,
|
||||
onProviderStreamEvent?: ProviderEventObserver,
|
||||
@@ -325,6 +329,7 @@ async function streamOnce(
|
||||
// and tool declarations fold into a leading system message.
|
||||
// normalizeContext is the library's only Context→TranscriptContext entry.
|
||||
const stream = engine.streamFn(engine.model, normalizeContext({ systemPrompt, messages, tools }), {
|
||||
sessionId,
|
||||
reasoning: reasoning && reasoning !== "off" ? reasoning : undefined,
|
||||
signal: abortSignal,
|
||||
onProviderStreamEvent: onProviderStreamEvent as never,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { invalidInputResult, questionResult } from "./tool-execute.ts";
|
||||
import { providerEventObserver } from "./provider-events.ts";
|
||||
import type { Usage } from "@earendil-works/pi-ai";
|
||||
import { PATHS } from "../../config.ts";
|
||||
import { piEngineFor, type PiEngine } from "./models.ts";
|
||||
import { APP_IDENTITY, piEngineFor, type PiEngine } from "./models.ts";
|
||||
import { effectivePromptSegments } from "./prompt-segments.ts";
|
||||
import {
|
||||
askUserQuestionTool,
|
||||
@@ -116,7 +116,9 @@ export interface AgentBuildOptions {
|
||||
* fresh per turn, honoring enabled overrides (switch on + file
|
||||
* exists); absent → the cached pure-source composition. */
|
||||
promptSwitches?: Record<string, boolean>;
|
||||
/** Debug: when set AND LH_PROVIDER_EVENTS=1, raw provider stream events
|
||||
/** Native conversation identity: the existing persisted chat id, reused
|
||||
* across reconstructed prober, teacher, and practice Agents.
|
||||
* Debug: when set AND LH_PROVIDER_EVENTS=1, raw provider stream events
|
||||
* (pre-normalization) append to data/debug/provider-events/
|
||||
* <chatId>.jsonl via providerEventObserver — the GLM-truncation /
|
||||
* DeepSeek-id incident layer. Absent or env-off → no observer. */
|
||||
@@ -177,6 +179,8 @@ function baseAgentOptions(kind: AgentKind, opts: AgentBuildOptions, tools: Agent
|
||||
// the normal no-observer path (tests, env-off).
|
||||
const providerEvents = opts.chatId ? providerEventObserver(opts.chatId, kind) : undefined;
|
||||
const agent = new Agent({
|
||||
identity: APP_IDENTITY,
|
||||
sessionId: opts.chatId,
|
||||
initialState: {
|
||||
systemPrompt,
|
||||
model: engine.model,
|
||||
|
||||
@@ -77,7 +77,7 @@ export interface AuthProviderInfo {
|
||||
*
|
||||
* EXCLUDED by owner spec — providers whose auth is not a plain key this
|
||||
* wave: github-copilot (OAuth), amazon-bedrock (AWS creds), google-vertex
|
||||
* (ADC), azure-openai-responses (needs a base URL), the cloudflare
|
||||
* (ADC), azure (needs a base URL), the cloudflare
|
||||
* account-scoped pair. Anthropic keeps ANTHROPIC_API_KEY only — its
|
||||
* AUTH_TOKEN/OAUTH_TOKEN alternates stay env-only. openai-codex exposes
|
||||
* no env-key (naturally absent). Providers sharing one env var (moonshotai
|
||||
|
||||
+318
-256
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user