npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

gpt-maa-ts

v0.4.2

Published

A TypeScript client for submitting AgentDefinitions and user prompt to a gpt-multi-atomic-agents REST API, to generate supported mutations.

Downloads

1,025

Readme

gpt-maa-ts README (gpt-multi-atomic-agents - TypeScript Client)

A TypeScript client for submitting AgentDefinitions and user prompts to a gpt-multi-atomic-agents REST API, to generate supported mutations (function calls).

This client provides a mini framework for defining the Agents and handling the response.

This provides a clean approach to LLM based Agent calling, so the client can focus on the 'domain' or business logic:

  • submit data in the form of Function Calls
  • process the generated mutations, updating the application data

npm package

Example

As an Example, consider the domain of household maintenance agents which operate a Lawn Mower robot and a Waste Disposer robot.

There is one agent for mowing the lawn (the 'Lawn Mower' agent), and another agent for disposing of waste (the 'Waster Disposer' agent).

By collaborating together, the agents can complete the greater task of clearing and mowing the lawn, and then cleaning up afterwards.

Agents are declared in terms of input and output functions.

First, we define the functions which the Lawn Mower agent will use:

const areaParameter: ParameterSpec = {
    name: "area",
    type: "string",
    allowedValues: ["front", "back"],
}

const mowLawnFunction: FunctionSpecSchema = {
    functionName: "MowLawn",
    description: "Mow the lawn and tidy it up",
    parameters: [areaParameter]
}
const produceCutGrassFunction: FunctionSpecSchema = {
    functionName: "ProduceCutGrass",
    description: "Produce cut grass waster",
    parameters: [areaParameter]
}

Next, we can define the Lawn Mower Agent in terms of Functions (as inputs and outputs):

const lawnMowerAgent: FunctionAgentDefinitionMinimal = {
    agentName: "Lawn Mower",
    description: "Knows how to mow lawns",
    acceptedFunctions: [mowLawnFunction, produceCutGrassFunction],
    functionsAllowedToGenerate: [mowLawnFunction, produceCutGrassFunction],
    topics: ["garden", "lawn", "grass"],
}

NOTE: Notice than an agent is basically a JSON document, so it can be imported, exported and even edited between REST API calls.

Next we define Functions that are specific to the Waste Disposer agent:

const wasteDisposerOutputFunctions: FunctionSpecSchema[] = [
    {
        functionName: "CollectWaste",
        description: "Collect waste from an area",
        parameters: [areaParameter]
    },
    {
        functionName: "TakeWasteToCollectionSite",
        description: "Take previously collected waste from an area, to a collection site",
        parameters: [areaParameter, {
            name: "site_name",
            type: "string"
        }]
    }
]

Now that we have the waster disposer functions, and the waste output function of the Lawn Mower, we can define the Waste Disposer agent.

const wasteDisposerAgent: FunctionAgentDefinitionMinimal = {
    agentName: "Waste Disposer",
    description: "Knows how to collect and dispose of waste",
    acceptedFunctions: [...wasteDisposerOutputFunctions, produceCutGrassFunction], // The waste disposer can observe when the lawn-mower has generated waste
    functionsAllowedToGenerate: wasteDisposerOutputFunctions,
    topics: ["waste", "dispose"],
}

Notice that the Waste Disposer agent takes the produceCutGrassFunction function as in an input. This allows the Waste Disposer agent to see and understand that part of the output from the Lawn Mower agent.

NOTE: By sharing a subset of Function Calls, agents are able to understand each other's output, and collaborate indirectly via the REST API (internally, the REST API uses a Blackboard).

Finally, we also need to provide Handlers, to be able to execute the generated function calls.

NOTE: Handlers are the main point of integration with the greater application. Handlers are 'fixed' since they are necessarily hard-coded against the greater application, whereas Agents are more dynamic.

const functionRegistry = new FunctionRegistry();

class LawnHandler extends AreaHandlerBase
{
    constructor(registry: FunctionRegistry) {
        super(registry);
        this.registerFunction(mowLawnFunction.functionName!, this.handleMowLawn)
        this.registerFunction(produceCutGrassFunction.functionName!, this.handleProduceCutGrass)
    }

    private handleMowLawn(functionCall: FunctionCallSchema): void {
        console.log("<mowing the lawn>")
        console.log(`  params:`, functionCall.parameters)
    }

    private handleProduceCutGrass(functionCall: FunctionCallSchema): void {
        console.log("<producing cut grass>")
        console.log(`  params:`, functionCall.parameters)
    }

    protected nameImplementation(): string
    {
        return "Lawn Handler";
    }
}

// Create the handlers (they register themselves)
const defaultHandler = new DefaultAreaHandler(functionRegistry, (functionCall: FunctionCallSchema) => {
    console.log(`[default handler] for function call: ${functionCall.functionName}`, functionCall.parameters);
});
const lawnHandler = new LawnHandler(functionRegistry);
const wasteHandler = defaultHandler; // We can later add specific handling for Waste functions.

Now, we can use the agents to generate function calls, and execute them:

const agentDefinitions: FunctionAgentDefinitionMinimal[] = [
    lawnMowerAgent, wasteDisposerAgent
]

// =================================================
// Chat with the Agents
const chatAgentDescription = "Handles questions about household chores such as garden, garden furniture and waste maintenance.";

const blackboardAccessor = await handleUserPrompt("Mow the lawn, dealing with any lawn furniture and waste. After mowing make sure waste is disposed of.", agentDefinitions, chatAgentDescription)

// =================================================
// Display the messages from the Agents
console.log(blackboardAccessor.get_new_messages());

// =================================================
// Execute the Function Calls using our Handlers
blackboardAccessor.get_new_functions()
const onExecuteStart = (): Promise<ExecuteStartResult> => {
    console.log("(execution started)");
    return {
        isOkToContinue: true,
        alsoExecutePreviousFunctions: false
    }
}
const onExecuteEnd = (errors: ExecutionError[], blackboardAccessor: FunctionCallBlackboardAccessor): Promise<void> => {
    console.log("(execution ended)");

    if (errors.length) {
      console.error(errors);
    }

    // Assuming that client has applied all functions, and wants to continue from that state:
    const new_user_data = blackboardAccessor.get_new_functions();
    blackboardAccessor.set_user_data(new_user_data);
}
execute(blackboardAccessor, functionRegistry, onExecuteStart, onExecuteEnd);

NOTE: The Blackboard is serialized back to the client, in order to avoid making the server statefull. If that produces heavy network traffic, then future versions of the server may allow for state-full operation (however this comes with tradeoffs).

For more details, see TypeScript Example Agents.

Setup (for consuming as a package)

Depdencencies:

NOTE: You need a copy of the config.gpt-maa-ts.json file in your folder.

NOTE: You need to ensure that any directories referred to by config.gpt-maa-ts.json have been created.

NOTE: You need to have the gpt-multi-atomic-agents REST API running.

Usage

To run a REPL loop:

./go.sh

NOTE: 'Dynamic' Custom Agents are stored locally in the data-agents folder. Even while running the REPL, you can edit the agent files or add new ones. If you edit or add a custom agent, then use the REPL command to reload agents. There is also a command to list the active agents.

For examples, see the TypeScript Example Agents.

Test

./test.sh