Skip to content

Commit

Permalink
working towards serverless agents
Browse files Browse the repository at this point in the history
  • Loading branch information
nalbion committed Feb 14, 2024
1 parent 048a490 commit 63775a8
Show file tree
Hide file tree
Showing 12 changed files with 3,872 additions and 1,367 deletions.
5,123 changes: 3,778 additions & 1,345 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agent-adapters",
"version": "1.0.1",
"version": "1.0.2",
"description": "Configurable AI Agents",
"license": "MPL-2.0",
"main": "dist/index.js",
Expand Down Expand Up @@ -41,9 +41,16 @@
"url": "https://github.com/nalbion/agent-adapters/issues"
},
"homepage": "https://github.com/nalbion/agent-adapters#readme",
"files": [
"dist/",
"README.md",
"package.json",
"LICENSE.txt"
],
"dependencies": {
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"any-cloud-storage": "^1.0.3",
"js-yaml": "^4.1.0",
"rimraf": "^5.0.5",
"yaml-ast-parser": "^0.0.43"
Expand Down
11 changes: 11 additions & 0 deletions src/agents/adapters/AgentProtocolServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ import { AgentConfig } from '../../types';

type AgentConfigWithRequiredServer = Required<Pick<AgentConfig, 'server'>> & Partial<Omit<AgentConfig, 'server'>>;

/**
* Runs as a singleton service, listening for Agent Protocol requests.
*
* If `config.server.port` is provided, it will start a server to listen for incoming requests.
* Otherwise, serverless Agents need to manually call `createServerless` and provide to the Serverless provider.
*
* @see ./firebase, google-cloud, lambda
* @see https://github.com/AI-Engineer-Foundation/agent-protocol/pull/99
* @see https://github.com/AI-Engineer-Foundation/agent-protocol/pull/103
* @see https://github.com/AI-Engineer-Foundation/agent-protocol/pull/104
*/
export default class AgentProtocolServer extends Agent {
private static instance: AgentProtocolServer | undefined;
private taskContexts: Map<string, AgentProtocolContext> = new Map();
Expand Down
30 changes: 27 additions & 3 deletions src/agents/adapters/agentprotocol/agentprotocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Agent, { type StepHandler, type StepInput, type StepResult, type TaskInpu
import AgentProtocolServer from '../AgentProtocolServer';
import { logger } from '../../../utils/Logger';
import { AgentResponseStatus } from '../../types/AgentResponse';
import { FileStorage } from 'any-cloud-storage';

/**
* Handles incoming requests: POST /ap/v1/agent/task
Expand Down Expand Up @@ -37,13 +38,36 @@ export const startServer = (config: { port?: number; workspace?: string }) => {
apAgent.start();
};

export const createServerless = (config: {
type ServerRequestHandler<R, S> = (request: R, response: S) => void | Promise<void>;
type BuildServerConfig = {
workspace?: string;
// Waiting on https://github.com/AI-Engineer-Foundation/agent-protocol/pull/100
// artifactStorage: ArtifactStorage;
}) => {
};

const buildServer = (config: BuildServerConfig) => {
apAgent = Agent.handleTask(taskHandler, config);
// Waiting on https://github.com/AI-Engineer-Foundation/agent-protocol/pull/99
// return agent.build();
const apHandler = (apAgent as any).build() as ServerRequestHandler<R, S>; // the Express app
apAgent.start();
return apHandler;
};

export const createServerless = <R = Request, S = Response>(
config: BuildServerConfig,
promisedStorage?: Promise<FileStorage>,
) => {
if (promisedStorage) {
const promisedHandler = promisedStorage.then((storage) => {
// config.artifactStorage = ArtifactStorageFactory.create(config.artifactStorage);
// config.artifactStorage = new AnyCloudArtifactStorage(storage);
return buildServer(config);
});

return (request: R, response: S) => promisedHandler.then((handler) => handler(request, response));
}

return buildServer(config);
};

// type Artifact = {
Expand Down
16 changes: 11 additions & 5 deletions src/agents/adapters/agentprotocol/firebase.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { onRequest } from 'firebase-functions/v2/https';
import { Request, onRequest } from 'firebase-functions/v2/https';
import { createServerless } from './agentprotocol';
import { useAnyCloudStorage } from '../../../utils/fileStorage';

const app = createServerless({
// artifactStorage: new FirebaseStorage(storage),
});
// export const agentprotocol = onRequest(app);
const promisedStorage = useAnyCloudStorage({ type: 'firebase', bucket: 'my-bucket' });

const app = createServerless<Request, Express.Response>(
{
// artifactStorage: new AnyCloudArtifactStorage(storage),
},
promisedStorage,
);
export const agentprotocol = onRequest(app);
12 changes: 9 additions & 3 deletions src/agents/adapters/agentprotocol/google-cloud.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import functions from '@google-cloud/functions-framework';
import { createServerless } from './agentprotocol';
import { useAnyCloudStorage } from '../../../utils/fileStorage';

const app = createServerless({
// artifactStorage: new GcpStorage(storage, "my-agent-artifacts")
});
const promisedStorage = useAnyCloudStorage({ type: 'gcp', bucket: 'my-bucket' });

const app = createServerless(
{
// artifactStorage: new AnyCloudArtifactStorage(storage)
},
promisedStorage,
);
// functions.http('agentprotocol', app);
12 changes: 9 additions & 3 deletions src/agents/adapters/agentprotocol/lambda.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import awsServerlessExpress from 'aws-serverless-express';
import { createServerless } from './agentprotocol';
import { APIGatewayProxyEvent, Context } from 'aws-lambda';
import { useAnyCloudStorage } from '../../../utils/fileStorage';

const app = createServerless({
// artifactStorage: new S3Storage(s3, 'my-agent-artifacts'),
});
const promisedStorage = useAnyCloudStorage({ type: 's3', bucket: 'my-bucket' });

const app = createServerless(
{
// artifactStorage: new AnyCloudArtifactStorage(storage),
},
promisedStorage,
);
// const server = awsServerlessExpress.createServer(app);

// exports.handler = (event: APIGatewayProxyEvent, context: Context) => {
Expand Down
11 changes: 5 additions & 6 deletions src/tools/impl/write_file.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import path from 'path';
import { ToolManager } from '../ToolManager';
import { ToolContext } from '../tool_types';
import { mkdirSync, writeFileSync } from 'fs';
import { getAbsolutePathInWorkspace } from '../../utils/fileUtils';
import { fileStorage } from '../../utils/fileStorage';

export const TOOL_WRITE_FILE = 'write_file';

Expand All @@ -11,12 +11,11 @@ export const TOOL_WRITE_FILE = 'write_file';
* @param filename The name of the file to write
* @param contents The contents of the file to write
*/
const write_file = (context: ToolContext, filename: string, contents: string) => {
const write_file = async (context: ToolContext, filename: string, contents: string) => {
const filePath = getAbsolutePathInWorkspace(context.workspaceFolder, filename);
const dir = path.dirname(filePath);
// TODO: abstract to ArtifactStorage - https://github.com/AI-Engineer-Foundation/agent-protocol/pull/100
mkdirSync(dir, { recursive: true });
writeFileSync(filePath, contents, { encoding: 'utf-8' });

await fileStorage.saveTextFile(filePath, contents);

context.onProgress({ type: 'inlineContentReference', title: filename, inlineReference: filePath });
};

Expand Down
2 changes: 1 addition & 1 deletion src/tools/tool_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ export type ToolConfig = {
export type ToolCallback = (
context: ToolContext,
...parameters: any
) => string | undefined | void | Promise<string | undefined>;
) => string | undefined | void | Promise<string | undefined | void>;
4 changes: 4 additions & 0 deletions src/types/AgentsYml.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable */

import { FileStorageConfig } from 'any-cloud-storage';
import { AIConfigRootMetadata, AiConfigPrompt, ModelSettings } from './AIConfig';

/**
Expand Down Expand Up @@ -100,6 +101,9 @@ export interface AgentConfig {
port?: number;
/** @default './workspace' */
workspace?: string;

/** By default, files will be saved to disk, but serverless agents can use alternatives using `any-cloud-storage` */
storage?: FileStorageConfig;
};

/**
Expand Down
8 changes: 8 additions & 0 deletions src/utils/fileStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import createStorage, { FileStorage, FileStorageConfig, LocalFileStorage } from 'any-cloud-storage';

export let fileStorage: FileStorage = new LocalFileStorage({ type: 'file', basePath: '' });

export const useAnyCloudStorage = async (config: FileStorageConfig) => {
fileStorage = await createStorage(config);
return fileStorage;
};
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"rootDir": "src",
"outDir": "./dist",
"sourceMap": true,
"declaration": true,
"strict": true,
"lib": ["ES2022"],
"types": ["node", "jest"],
Expand Down

0 comments on commit 63775a8

Please sign in to comment.