From 8418e8873033a653903e5adb445c8eb22271c945 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Thu, 30 Apr 2026 22:28:01 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Port=20R101=20setWorkingVisible=20API?= =?UTF-8?q?=20and=20R104=20Azure=20Cognitive=20Services=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SF-Task: S01/T01 --- .../src/providers/azure-openai-responses.ts | 23 +++++++++++++++++++ .../src/core/extensions/runner.ts | 1 + .../src/core/extensions/types.ts | 3 +++ .../controllers/extension-ui-controller.ts | 5 ++++ .../pi-coding-agent/src/modes/rpc/rpc-mode.ts | 6 +++++ 5 files changed, 38 insertions(+) diff --git a/packages/pi-ai/src/providers/azure-openai-responses.ts b/packages/pi-ai/src/providers/azure-openai-responses.ts index 7067f728b..1ef2a6664 100644 --- a/packages/pi-ai/src/providers/azure-openai-responses.ts +++ b/packages/pi-ai/src/providers/azure-openai-responses.ts @@ -135,6 +135,24 @@ function buildDefaultBaseUrl(resourceName: string): string { return `https://${resourceName}.openai.azure.com/openai/v1`; } +function isCognitiveServicesDomain(url: string): boolean { + try { + const hostname = new URL(url).hostname; + return hostname.endsWith(".cognitiveservices.azure.com"); + } catch { + return false; + } +} + +function normalizeCognitiveServicesUrl(url: string): string { + // Azure Cognitive Services endpoints use /openai/deployments/{deployment}/chat/completions + // We need to normalize to the OpenAI-compatible base path + if (url.includes("/openai/deployments/")) { + return url.split("/openai/deployments/")[0]!; + } + return url; +} + function resolveAzureConfig( model: Model<"azure-openai-responses">, options?: AzureOpenAIResponsesOptions, @@ -160,6 +178,11 @@ function resolveAzureConfig( ); } + // Normalize Cognitive Services endpoints (e.g., .cognitiveservices.azure.com) + if (isCognitiveServicesDomain(resolvedBaseUrl)) { + resolvedBaseUrl = normalizeCognitiveServicesUrl(resolvedBaseUrl); + } + return { baseUrl: normalizeAzureBaseUrl(resolvedBaseUrl), apiVersion, diff --git a/packages/pi-coding-agent/src/core/extensions/runner.ts b/packages/pi-coding-agent/src/core/extensions/runner.ts index c0f2f9ff6..ba23ad220 100644 --- a/packages/pi-coding-agent/src/core/extensions/runner.ts +++ b/packages/pi-coding-agent/src/core/extensions/runner.ts @@ -166,6 +166,7 @@ const noOpUIContext: ExtensionUIContext = { onTerminalInput: () => () => {}, setStatus: () => {}, setWorkingMessage: () => {}, + setWorkingVisible: () => {}, setWidget: () => {}, setFooter: () => {}, setHeader: () => {}, diff --git a/packages/pi-coding-agent/src/core/extensions/types.ts b/packages/pi-coding-agent/src/core/extensions/types.ts index 3aadf88ec..a533323ce 100644 --- a/packages/pi-coding-agent/src/core/extensions/types.ts +++ b/packages/pi-coding-agent/src/core/extensions/types.ts @@ -131,6 +131,9 @@ export interface ExtensionUIContext { /** Set the working/loading message shown during streaming. Call with no argument to restore default. */ setWorkingMessage(message?: string): void; + /** Show or hide the built-in working/loading indicator. */ + setWorkingVisible(visible: boolean): void; + /** Set a widget to display above or below the editor. Accepts string array or component factory. */ setWidget(key: string, content: string[] | undefined, options?: ExtensionWidgetOptions): void; setWidget( diff --git a/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts b/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts index c4ce6c527..50b9dda3b 100644 --- a/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +++ b/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts @@ -22,6 +22,11 @@ export function createExtensionUIContext(host: any): ExtensionUIContext { host.pendingWorkingMessage = message; } }, + setWorkingVisible: (visible) => { + if (host.loadingAnimation) { + host.loadingAnimation.setVisible(visible); + } + }, setWidget: (key, content, options) => host.setExtensionWidget(key, content, options), setFooter: (factory) => host.setExtensionFooter(factory), setHeader: (factory) => host.setExtensionHeader(factory), diff --git a/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts b/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts index 4de55c659..e6666f3a7 100644 --- a/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +++ b/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts @@ -290,6 +290,12 @@ export async function runRpcMode(session: AgentSession): Promise { }); }, + setWorkingVisible(visible: boolean): void { + void withEmbeddedUiContext((ui) => { + ui.setWorkingVisible(visible); + }); + }, + setWidget(key: string, content: unknown, options?: ExtensionWidgetOptions): void { widgetState.set(key, { content, options }); if (content === undefined || Array.isArray(content)) {