Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 5 additions & 14 deletions apps/angular/demo-server/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { cors } from "hono/cors";
import {
CopilotRuntime,
createCopilotEndpoint,
InMemoryAgentRunner,
} from "@copilotkitnext/runtime";
import {
OpenAIAgent,
SlowToolCallStreamingAgent,
} from "@copilotkitnext/demo-agents";
import { CopilotRuntime, createCopilotEndpoint, InMemoryAgentRunner } from "@copilotkitnext/runtime";
import { OpenAIAgent, SlowToolCallStreamingAgent } from "@copilotkitnext/demo-agents";

const runtime = new CopilotRuntime({
agents: {
// @ts-ignore
default: new SlowToolCallStreamingAgent(),
default: new OpenAIAgent(),
},
runner: new InMemoryAgentRunner(),
});
Expand All @@ -32,7 +25,7 @@ app.use(
exposeHeaders: ["Content-Type"],
credentials: true,
maxAge: 86400,
})
}),
);

// Create the CopilotKit endpoint
Expand All @@ -46,6 +39,4 @@ app.route("/", copilotApp);

const port = Number(process.env.PORT || 3001);
serve({ fetch: app.fetch, port });
console.log(
`CopilotKit runtime listening at http://localhost:${port}/api/copilotkit`
);
console.log(`CopilotKit runtime listening at http://localhost:${port}/api/copilotkit`);
1 change: 1 addition & 0 deletions apps/angular/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@copilotkitnext/angular": "workspace:*",
"rxjs": "^7.8.1",
"tslib": "^2.8.1",
"zod": "^3.25.75",
"zone.js": "^0.14.0"
},
"devDependencies": {
Expand Down
11 changes: 4 additions & 7 deletions apps/angular/demo/src/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { ApplicationConfig, importProvidersFrom } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import {
provideCopilotKit,
provideCopilotChatLabels,
} from "@copilotkitnext/angular";
import { provideCopilotKit, provideCopilotChatLabels } from "@copilotkitnext/angular";
import { WildcardToolRenderComponent } from "./components/wildcard-tool-render.component";
import { helloWorldToolConfig } from "./tools/hello-world";

export const appConfig: ApplicationConfig = {
providers: [
Expand All @@ -17,13 +15,12 @@ export const appConfig: ApplicationConfig = {
component: WildcardToolRenderComponent,
} as any,
],
frontendTools: [],
frontendTools: [helloWorldToolConfig],
humanInTheLoop: [],
}),
provideCopilotChatLabels({
chatInputPlaceholder: "Ask me anything...",
chatDisclaimerText:
"CopilotKit Angular Demo - AI responses may need verification.",
chatDisclaimerText: "CopilotKit Angular Demo - AI responses may need verification.",
}),
],
};
27 changes: 27 additions & 0 deletions apps/angular/demo/src/app/tools/hello-world.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Component, input } from "@angular/core";
import { FrontendToolConfig, ToolRenderer } from "@copilotkitnext/angular";
import { JsonPipe } from "@angular/common";
import { AngularToolCall } from "@copilotkitnext/angular";
import { z } from "zod";

@Component({
selector: "app-hello-world-tool",
template: ` <pre>{{ toolCall() | json }}</pre> `,
standalone: true,
imports: [JsonPipe],
})
export class HelloWorldTool implements ToolRenderer {
toolCall = input.required<AngularToolCall>();
}

export const helloWorldToolConfig: FrontendToolConfig = {
name: "hello_world",
description: "Says hello to the world",
args: z.object({
name: z.string(),
}),
component: HelloWorldTool,
handler: async (args) => {
return `Hello ${args.name}!`;
},
};
5 changes: 4 additions & 1 deletion apps/react/demo/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ function Chat() {

const greeting = "Hello Copilot! 👋 Could you help me with something?";

const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value")?.set;
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLTextAreaElement.prototype,
"value",
)?.set;
nativeInputValueSetter?.call(textarea, greeting);
textarea.dispatchEvent(new Event("input", { bubbles: true }));
textarea.focus();
Expand Down
25 changes: 5 additions & 20 deletions packages/angular/src/lib/agent.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import {
DestroyRef,
Injectable,
inject,
signal,
computed,
Signal,
} from "@angular/core";
import { DestroyRef, Injectable, inject, signal, computed, Signal } from "@angular/core";
import { CopilotKit } from "./copilotkit";
import type { AbstractAgent } from "@ag-ui/client";
import type { Message } from "@ag-ui/client";
Expand Down Expand Up @@ -61,10 +54,7 @@ export class AgentStore {
export class CopilotkitAgentFactory {
readonly #copilotkit = inject(CopilotKit);

createAgentStoreSignal(
agentId: Signal<string | undefined>,
destroyRef: DestroyRef
): Signal<AgentStore | undefined> {
createAgentStoreSignal(agentId: Signal<string | undefined>, destroyRef: DestroyRef): Signal<AgentStore | undefined> {
let lastAgentStore: AgentStore | undefined;

return computed(() => {
Expand All @@ -75,9 +65,7 @@ export class CopilotkitAgentFactory {
lastAgentStore = undefined;
}

const abstractAgent = this.#copilotkit.getAgent(
agentId() || DEFAULT_AGENT_ID
);
const abstractAgent = this.#copilotkit.getAgent(agentId() || DEFAULT_AGENT_ID);
if (!abstractAgent) return undefined;

lastAgentStore = new AgentStore(abstractAgent, destroyRef);
Expand All @@ -86,13 +74,10 @@ export class CopilotkitAgentFactory {
}
}

export function injectAgentStore(
agentId: string | Signal<string | undefined>
): Signal<AgentStore | undefined> {
export function injectAgentStore(agentId: string | Signal<string | undefined>): Signal<AgentStore | undefined> {
const agentFactory = inject(CopilotkitAgentFactory);
const destroyRef = inject(DestroyRef);
const agentIdSignal =
typeof agentId === "function" ? agentId : computed(() => agentId);
const agentIdSignal = typeof agentId === "function" ? agentId : computed(() => agentId);

return agentFactory.createAgentStoreSignal(agentIdSignal, destroyRef);
}
86 changes: 23 additions & 63 deletions packages/angular/src/lib/copilotkit.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,7 @@
import { AbstractAgent } from "@ag-ui/client";
import { FrontendTool, CopilotKitCore } from "@copilotkitnext/core";
import {
Injectable,
Injector,
Signal,
WritableSignal,
runInInjectionContext,
signal,
inject,
} from "@angular/core";
import {
FrontendToolConfig,
HumanInTheLoopConfig,
RenderToolCallConfig,
} from "./tools";
import { Injectable, Injector, Signal, WritableSignal, runInInjectionContext, signal, inject } from "@angular/core";
import { FrontendToolConfig, HumanInTheLoopConfig, RenderToolCallConfig } from "./tools";
import { injectCopilotKitConfig } from "./config";
import { HumanInTheLoop } from "./human-in-the-loop";

Expand All @@ -22,9 +10,7 @@ export class CopilotKit {
readonly #config = injectCopilotKitConfig();
readonly #hitl = inject(HumanInTheLoop);
readonly #rootInjector = inject(Injector);
readonly #agents = signal<Record<string, AbstractAgent>>(
this.#config.agents ?? {}
);
readonly #agents = signal<Record<string, AbstractAgent>>(this.#config.agents ?? {});
readonly agents = this.#agents.asReadonly();

readonly core = new CopilotKitCore({
Expand All @@ -35,18 +21,12 @@ export class CopilotKit {
tools: this.#config.tools,
});

readonly #toolCallRenderConfigs: WritableSignal<RenderToolCallConfig[]> =
signal([]);
readonly #clientToolCallRenderConfigs: WritableSignal<FrontendToolConfig[]> =
signal([]);
readonly #humanInTheLoopToolRenderConfigs: WritableSignal<
HumanInTheLoopConfig[]
> = signal([]);

readonly toolCallRenderConfigs: Signal<RenderToolCallConfig[]> =
this.#toolCallRenderConfigs.asReadonly();
readonly clientToolCallRenderConfigs: Signal<FrontendToolConfig[]> =
this.#clientToolCallRenderConfigs.asReadonly();
readonly #toolCallRenderConfigs: WritableSignal<RenderToolCallConfig[]> = signal([]);
readonly #clientToolCallRenderConfigs: WritableSignal<FrontendToolConfig[]> = signal([]);
readonly #humanInTheLoopToolRenderConfigs: WritableSignal<HumanInTheLoopConfig[]> = signal([]);

readonly toolCallRenderConfigs: Signal<RenderToolCallConfig[]> = this.#toolCallRenderConfigs.asReadonly();
readonly clientToolCallRenderConfigs: Signal<FrontendToolConfig[]> = this.#clientToolCallRenderConfigs.asReadonly();
readonly humanInTheLoopToolRenderConfigs: Signal<HumanInTheLoopConfig[]> =
this.#humanInTheLoopToolRenderConfigs.asReadonly();

Expand Down Expand Up @@ -84,38 +64,36 @@ export class CopilotKit {
#bindClientTool(
clientToolWithInjector: FrontendToolConfig & {
injector: Injector;
}
},
): FrontendTool {
const { injector, handler, ...frontendCandidate } = clientToolWithInjector;
const { injector, handler, args, description, name, agentId } = clientToolWithInjector;

return {
...frontendCandidate,
description,
name,
agentId,
handler: (args) => runInInjectionContext(injector, () => handler(args)),
parameters: args,
};
}

addFrontendTool(
clientToolWithInjector: FrontendToolConfig & {
injector: Injector;
}
},
): void {
const tool = this.#bindClientTool(clientToolWithInjector);

this.core.addTool(tool);

this.#clientToolCallRenderConfigs.update((current) => [
...current,
clientToolWithInjector,
]);
this.#clientToolCallRenderConfigs.update((current) => [...current, clientToolWithInjector]);
}

addRenderToolCall(renderConfig: RenderToolCallConfig): void {
this.#toolCallRenderConfigs.update((current) => [...current, renderConfig]);
}

#bindHumanInTheLoopTool(
humanInTheLoopTool: HumanInTheLoopConfig
): FrontendTool {
#bindHumanInTheLoopTool(humanInTheLoopTool: HumanInTheLoopConfig): FrontendTool {
return {
...humanInTheLoopTool,
handler: (args, toolCall) => {
Expand All @@ -125,20 +103,14 @@ export class CopilotKit {
}

addHumanInTheLoop(humanInTheLoopTool: HumanInTheLoopConfig): void {
this.#humanInTheLoopToolRenderConfigs.update((current) => [
...current,
humanInTheLoopTool,
]);
this.#humanInTheLoopToolRenderConfigs.update((current) => [...current, humanInTheLoopTool]);

const tool = this.#bindHumanInTheLoopTool(humanInTheLoopTool);

this.core.addTool(tool);
}

#isSameAgentId<T extends { agentId?: string }>(
target: T,
agentId?: string
): boolean {
#isSameAgentId<T extends { agentId?: string }>(target: T, agentId?: string): boolean {
if (agentId) {
return target.agentId === agentId;
}
Expand All @@ -149,25 +121,13 @@ export class CopilotKit {
removeTool(toolName: string, agentId?: string): void {
this.core.removeTool(toolName);
this.#clientToolCallRenderConfigs.update((current) =>
current.filter(
(renderConfig) =>
renderConfig.name !== toolName &&
this.#isSameAgentId(renderConfig, agentId)
)
current.filter((renderConfig) => renderConfig.name !== toolName && this.#isSameAgentId(renderConfig, agentId)),
);
this.#humanInTheLoopToolRenderConfigs.update((current) =>
current.filter(
(renderConfig) =>
renderConfig.name !== toolName &&
this.#isSameAgentId(renderConfig, agentId)
)
current.filter((renderConfig) => renderConfig.name !== toolName && this.#isSameAgentId(renderConfig, agentId)),
);
this.#toolCallRenderConfigs.update((current) =>
current.filter(
(renderConfig) =>
renderConfig.name !== toolName &&
this.#isSameAgentId(renderConfig, agentId)
)
current.filter((renderConfig) => renderConfig.name !== toolName && this.#isSameAgentId(renderConfig, agentId)),
);
}

Expand Down
Loading
Loading