diff --git a/samples/typescript/hosted-agents/agent-framework/responses/13-foundry-memory/README.md b/samples/typescript/hosted-agents/agent-framework/responses/13-foundry-memory/README.md index 899eca6..159af2c 100644 --- a/samples/typescript/hosted-agents/agent-framework/responses/13-foundry-memory/README.md +++ b/samples/typescript/hosted-agents/agent-framework/responses/13-foundry-memory/README.md @@ -12,9 +12,9 @@ Ported from the Python sample [`hosted-agents/agent-framework/responses/13-found 2. **Searches for contextual memories** matching the current user message and injects them into the model context. 3. **Queues a store update** with new facts inferred from the conversation. -After each successful run, an Agent Framework middleware calls `FoundryMemoryProvider.whenUpdatesCompleted()` to wait for the queued extraction. This makes the new memories searchable before the next request without adding sample-specific polling logic or environment variables. +Memory extraction is asynchronous. Like the .NET hosted-agent sample, this agent does not block every response while a queued update is processed. Callers that immediately verify a newly taught memory should allow time for extraction to complete. -Memories are scoped per end user with `hostedUserScope()`: the hosting infrastructure injects the end-user id on every request. For local requests without that header, `MEMORY_USER_ID` supplies a stable development scope from `.env`. This is the TypeScript counterpart of the Python sample's `scope="{{$userId}}"` placeholder. +Memories are scoped per end user with `hostedUserScope()`: the hosting infrastructure injects the end-user id on every request. For local requests without that header, `MEMORY_USER_ID` supplies a stable development scope from `.env`. This is the TypeScript equivalent of .NET's `HostedFoundryMemoryProviderScopes.PerUser()`: it resolves the platform-injected end-user id to an explicit Memory scope instead of relying on the service-side `{{$userId}}` substitution used by the Python sample. The agent uses `FoundryChatClient` and is served via `ResponsesHostServer`, which exposes a REST API compatible with the OpenAI Responses container protocol v2.0.0. `serve` binds `0.0.0.0:${PORT:-8088}` — the address the platform's readiness probe expects. See [src/main.ts](src/main.ts). @@ -68,10 +68,12 @@ Invoke the local agent from another terminal: ```bash curl -X POST localhost:8088/responses -H 'content-type: application/json' -d '{"input":"Hi, my name is Alex and I am vegetarian."}' +# Memory extraction is asynchronous; wait before verifying a newly taught memory. +sleep 30 curl -X POST localhost:8088/responses -H 'content-type: application/json' -d '{"input":"Do you remember my name and what I like to eat?"}' ``` -Each successful response waits for asynchronous extraction through the Agent Framework's `whenUpdatesCompleted()` API. This mirrors the explicit `WhenUpdatesCompletedAsync()` calls in the .NET Foundry Memory sample and avoids relying on an arbitrary sleep before the next request. +`FoundryMemoryProvider.whenUpdatesCompleted()` is available for deterministic tests or one-shot workflows that must verify an update immediately, but the hosted server intentionally leaves extraction off the request's critical path. The sample fails fast when the store cannot be read or updated instead of silently behaving like a stateless agent. For local requests, `MEMORY_USER_ID` from `.env` is used when no `x-agent-user-id` header is present. diff --git a/samples/typescript/hosted-agents/agent-framework/responses/13-foundry-memory/src/main.ts b/samples/typescript/hosted-agents/agent-framework/responses/13-foundry-memory/src/main.ts index 5ba7426..d65321a 100644 --- a/samples/typescript/hosted-agents/agent-framework/responses/13-foundry-memory/src/main.ts +++ b/samples/typescript/hosted-agents/agent-framework/responses/13-foundry-memory/src/main.ts @@ -11,7 +11,7 @@ * variable. */ -import { Agent, agentMiddleware } from '@polymind-inc/agent-framework'; +import { Agent } from '@polymind-inc/agent-framework'; import { serve } from '@polymind-inc/agent-framework/agentserver/node'; import { FoundryChatClient, FoundryMemoryProvider } from '@polymind-inc/agent-framework/foundry'; import { hostedUserScope, ResponsesHostServer } from '@polymind-inc/agent-framework/foundry/hosting'; @@ -57,7 +57,8 @@ function resolveMemoryScope(): string { if (localMemoryUserId) return localMemoryUserId; throw new Error( 'No hosted end-user id is available. Set MEMORY_USER_ID in .env for local runs, ' + - 'or send x-agent-user-id with the request.', + 'or, for local testing only, send x-agent-user-id directly to the local server. ' + + 'In Foundry, the platform injects this header.', { cause: error }, ); } @@ -72,27 +73,6 @@ const memoryProvider = new FoundryMemoryProvider({ failureMode: 'throw', }); -// Foundry extracts memories asynchronously. Match the .NET sample's explicit -// WhenUpdatesCompletedAsync calls by waiting after each successful agent run. -// Streaming runs finish later, so register the wait against their final result. -const waitForMemoryUpdates = agentMiddleware( - async (ctx, next) => { - if (ctx.stream) { - ctx.onResult(async (response) => { - await memoryProvider.whenUpdatesCompleted(ctx.signal === undefined ? {} : { signal: ctx.signal }); - return response; - }); - } - - await next(); - - if (!ctx.stream) { - await memoryProvider.whenUpdatesCompleted(ctx.signal === undefined ? {} : { signal: ctx.signal }); - } - }, - { name: 'waitForFoundryMemoryUpdates' }, -); - const agent = new Agent({ client: new FoundryChatClient({ projectEndpoint, @@ -104,7 +84,6 @@ const agent = new Agent({ 'automatically provided to you in the system context. Use them when ' + 'answering, and acknowledge when you are relying on remembered facts.', contextProviders: [memoryProvider], - middleware: [waitForMemoryUpdates], // History will be managed by the hosting infrastructure, thus there // is no need to store history by the service. Learn more at: // https://developers.openai.com/api/reference/resources/responses/methods/create