Browse Source

add GettingStarted for typescript

pull/5678/head
Ryan Sweet 1 year ago
parent
commit
a321e2f9ba
12 changed files with 207 additions and 8 deletions
  1. +6
    -1
      typescript/contracts/IAgentRuntime.ts
  2. +27
    -0
      typescript/core/AgentsApp.ts
  3. +11
    -2
      typescript/core/TypePrefixSubscriptionAttribute.ts
  4. +2
    -2
      typescript/core/TypeSubscription.ts
  5. +11
    -2
      typescript/core/TypeSubscriptionAttribute.ts
  6. +40
    -0
      typescript/samples/GettingStarted/Checker.ts
  7. +3
    -0
      typescript/samples/GettingStarted/CountMessage.ts
  8. +3
    -0
      typescript/samples/GettingStarted/CountUpdate.ts
  9. +30
    -0
      typescript/samples/GettingStarted/Modifier.ts
  10. +56
    -0
      typescript/samples/GettingStarted/index.ts
  11. +15
    -0
      typescript/samples/GettingStarted/package.json
  12. +3
    -1
      typescript/tsconfig.json

+ 6
- 1
typescript/contracts/IAgentRuntime.ts View File

@@ -28,5 +28,10 @@ export interface IAgentRuntime {
loadAgentStateAsync(agentId: AgentId, state: unknown): Promise<void>;
saveAgentStateAsync(agentId: AgentId): Promise<unknown>;

// ...additional methods from the .NET interface...
registerAgentFactoryAsync(
type: AgentType,
factoryFunc: (agentId: AgentId, runtime: IAgentRuntime) => Promise<IAgent>
): Promise<AgentType>;

addSubscriptionAsync(subscription: ISubscriptionDefinition): Promise<void>;
}

+ 27
- 0
typescript/core/AgentsApp.ts View File

@@ -31,6 +31,7 @@ export class AgentsAppBuilder {

export class AgentsApp {
private running = false;
private shutdownCallbacks: Array<() => void> = [];
constructor(public readonly runtime: IAgentRuntime) {}

@@ -60,4 +61,30 @@ export class AgentsApp {
}
await this.runtime.publishMessageAsync(message, topic, undefined, messageId);
}

async publishMessageAsync<T>(
message: T,
topic: TopicId,
messageId?: string
): Promise<void> {
if (!this.running) {
await this.start();
}
await this.runtime.publishMessageAsync(message, topic, undefined, messageId);
}

async waitForShutdown(): Promise<void> {
// Wait until shutdown is called
return new Promise((resolve) => {
if (!this.running) {
resolve();
} else {
const cleanup = () => {
this.running = false;
resolve();
};
this.shutdownCallbacks.push(cleanup);
}
});
}
}

+ 11
- 2
typescript/core/TypePrefixSubscriptionAttribute.ts View File

@@ -1,12 +1,21 @@
import "reflect-metadata";
import { ISubscriptionDefinition } from "../contracts/ISubscriptionDefinition";
import { IUnboundSubscriptionDefinition } from "../contracts/IUnboundSubscriptionDefinition";
import { AgentType } from "../contracts/AgentType";
import { TypePrefixSubscription } from "./TypePrefixSubscription";
import { TypePrefixSubscription as TypePrefixSubscriptionImpl } from "./TypePrefixSubscription";

// Export the decorator
export function TypePrefixSubscription(topic: string): ClassDecorator {
return function (target: Function): void {
// Store subscription metadata on the class
(Reflect as any).defineMetadata('subscription:topic:prefix', topic, target);
};
}

export class TypePrefixSubscriptionAttribute implements IUnboundSubscriptionDefinition {
constructor(private readonly topic: string) {}

bind(agentType: AgentType): ISubscriptionDefinition {
return new TypePrefixSubscription(this.topic, agentType);
return new TypePrefixSubscriptionImpl(this.topic, agentType);
}
}

+ 2
- 2
typescript/core/TypeSubscription.ts View File

@@ -7,9 +7,9 @@ export class TypeSubscription implements ISubscriptionDefinition {
private readonly agentType: AgentType;
public readonly id: string;

constructor(topicType: string, agentType: AgentType, id?: string) {
constructor(topicType: string, agentType?: AgentType, id?: string) {
this.topicType = topicType;
this.agentType = agentType;
this.agentType = agentType || topicType;
this.id = id ?? crypto.randomUUID();
}



+ 11
- 2
typescript/core/TypeSubscriptionAttribute.ts View File

@@ -1,12 +1,21 @@
import 'reflect-metadata';
import { ISubscriptionDefinition } from "../contracts/ISubscriptionDefinition";
import { IUnboundSubscriptionDefinition } from "../contracts/IUnboundSubscriptionDefinition";
import { AgentType } from "../contracts/AgentType";
import { TypeSubscription } from "./TypeSubscription";
import { TypeSubscription as TypeSubscriptionImpl } from "./TypeSubscription";

// Export the decorator
export function TypeSubscription(topic: string): ClassDecorator {
return function (target: Function): void {
// Store subscription metadata on the class
(Reflect as any).defineMetadata('subscription:topic', topic, target);
};
}

export class TypeSubscriptionAttribute implements IUnboundSubscriptionDefinition {
constructor(private readonly topic: string) {}

bind(agentType: AgentType): ISubscriptionDefinition {
return new TypeSubscription(this.topic, agentType);
return new TypeSubscriptionImpl(this.topic, agentType);
}
}

+ 40
- 0
typescript/samples/GettingStarted/Checker.ts View File

@@ -0,0 +1,40 @@
import { BaseAgent } from "../../core/BaseAgent";
import { IHandle } from "../../contracts/IHandle";
import { AgentId, IAgentRuntime } from "../../contracts/IAgentRuntime";
import { MessageContext } from "../../contracts/MessageContext";
import { TypeSubscription } from "../../core/TypeSubscriptionAttribute";
import { CountMessage } from "./CountMessage";
import { CountUpdate } from "./CountUpdate";

type TerminationF = (x: number) => boolean;

@TypeSubscription("default")
export class Checker extends BaseAgent implements IHandle<CountUpdate> {
private runUntilFunc: TerminationF;
private shutdown: () => void;

constructor(
id: AgentId,
runtime: IAgentRuntime,
runUntilFunc: TerminationF,
shutdown: () => void
) {
super(id, runtime, "Checker");
this.runUntilFunc = runUntilFunc;
this.shutdown = shutdown;
}

async handleAsync(message: CountUpdate, context: MessageContext): Promise<void> {
if (!this.runUntilFunc(message.newCount)) {
console.log(`\nChecker:\n${message.newCount} passed the check, continue.`);
const countMessage: CountMessage = { content: message.newCount };
await this.runtime.publishMessageAsync(
countMessage,
{ type: "default", source: this.id.key }
);
} else {
console.log(`\nChecker:\n${message.newCount} failed the check, stopping.`);
this.shutdown();
}
}
}

+ 3
- 0
typescript/samples/GettingStarted/CountMessage.ts View File

@@ -0,0 +1,3 @@
export interface CountMessage {
content: number;
}

+ 3
- 0
typescript/samples/GettingStarted/CountUpdate.ts View File

@@ -0,0 +1,3 @@
export interface CountUpdate {
newCount: number;
}

+ 30
- 0
typescript/samples/GettingStarted/Modifier.ts View File

@@ -0,0 +1,30 @@
import { BaseAgent } from "../../core/BaseAgent";
import { IHandle } from "../../contracts/IHandle";
import { AgentId, IAgentRuntime } from "../../contracts/IAgentRuntime";
import { MessageContext } from "../../contracts/MessageContext";
import { TypeSubscription } from "../../core/TypeSubscriptionAttribute";
import { CountMessage } from "./CountMessage";
import { CountUpdate } from "./CountUpdate";

type ModifyF = (x: number) => number;

@TypeSubscription("default")
export class Modifier extends BaseAgent implements IHandle<CountMessage> {
private modifyFunc: ModifyF;

constructor(id: AgentId, runtime: IAgentRuntime, modifyFunc: ModifyF) {
super(id, runtime, "Modifier");
this.modifyFunc = modifyFunc;
}

async handleAsync(message: CountMessage, context: MessageContext): Promise<void> {
const newValue = this.modifyFunc(message.content);
console.log(`\nModifier:\nModified ${message.content} to ${newValue}`);

const updateMessage: CountUpdate = { newCount: newValue };
await this.runtime.publishMessageAsync(
updateMessage,
{ type: "default", source: this.id.key }
);
}
}

+ 56
- 0
typescript/samples/GettingStarted/index.ts View File

@@ -0,0 +1,56 @@
import "reflect-metadata";
import { AgentsApp, AgentsAppBuilder } from "../../core/AgentsApp";
import { Checker } from "./Checker";
import { Modifier } from "./Modifier";
import { CountMessage } from "./CountMessage";
import { TypeSubscription } from "../../core/TypeSubscription";
import { AgentId, IAgentRuntime } from "../../contracts/IAgentRuntime";

async function main() {
// Define the modification and termination functions
const modifyFunc = (x: number) => x - 1;
const runUntilFunc = (x: number) => x <= 1;

// Create and configure the app
const appBuilder = new AgentsAppBuilder()
.useInProcessRuntime(false); // Set deliverToSelf to false
const app = await appBuilder.build();

// Setup shutdown handler
const shutdown = () => {
app.shutdown();
};

// Register agents
await app.runtime.registerAgentFactoryAsync("Checker",
async (id: AgentId, runtime: IAgentRuntime) =>
new Checker(id, runtime, runUntilFunc, shutdown));
await app.runtime.registerAgentFactoryAsync("Modifier",
async (id: AgentId, runtime: IAgentRuntime) =>
new Modifier(id, runtime, modifyFunc));

// Add subscriptions
await app.runtime.addSubscriptionAsync(
new TypeSubscription("default")
);
await app.runtime.addSubscriptionAsync(
new TypeSubscription("default")
);

// Start the app
await app.start();

// Send initial message
await app.publishMessageAsync<CountMessage>(
{ content: 10 },
{ type: "default", source: "main" }
);

// Wait for shutdown
await app.waitForShutdown();
}

// Run the program
main().catch(console.error);

+ 15
- 0
typescript/samples/GettingStarted/package.json View File

@@ -0,0 +1,15 @@
{
"name": "autogen-getting-started",
"version": "1.0.0",
"description": "AutoGen Getting Started Sample",
"main": "index.js",
"scripts": {
"start": "ts-node index.ts"
},
"dependencies": {
"typescript": "^5.0.0",
"ts-node": "^10.9.0",
"reflect-metadata": "^0.1.13",
"@types/reflect-metadata": "^0.1.0"
}
}

+ 3
- 1
typescript/tsconfig.json View File

@@ -6,7 +6,9 @@
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "dist"
"outDir": "dist",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": [
"."


Loading…
Cancel
Save