Skip to content

Create a more intuitive workflow for defining custom functions #46

@chriscarrollsmith

Description

@chriscarrollsmith

Plan: Enhancing Custom Function Integration

Current Situation & Challenges

Currently, adding a new custom function callable by the OpenAI Assistant involves modifications across multiple parts of the codebase:

  • Function Logic (utils/custom_functions.py): Defining the Python function itself (e.g., get_weather).
  • Tool Definition (utils/custom_functions.py): Manually creating the FunctionToolParam data structure that describes the function to the OpenAI API.
  • Invocation Logic (routers/chat.py):
    • Importing the function.
    • Adding specific if/elif conditions to identify the function call by name (tool_call.function.name).
    • Manually parsing arguments for that specific function.
    • Calling the function.
    • Hardcoding the path to a Jinja2 template responsible for rendering the function's output.
  • Output Rendering (templates/components/<widget_name>.html): Creating a new HTML template for each function's output.

Proposed High-Level Design: A Tool Registration System

We propose a shift towards a more declarative and automated system for managing custom functions. This system would rely on developers registering their functions along with necessary metadata, allowing the application to handle the integration aspects more dynamically.

Key Components of the Proposed Design:

  1. Unified Tool Definition:

    • Developers would define their custom Python function.
    • Alongside the function, they would define its schema (parameters, description) for the OpenAI API. This could be achieved using Pydantic models for validation and schema generation, or a helper function/class that simplifies creating the FunctionDefinition.
    • Optionally, they would specify how the function's output should be rendered (e.g., by providing a path to a Jinja2 template or a dedicated rendering function).
  2. Centralized Tool Registry:

    • A central registry (e.g., a dictionary or a dedicated class) would store all custom functions and their metadata.
    • Functions would be "registered" with this system, perhaps via a decorator or by calling a registration method.
    • This registry would be the single source of truth for all available custom tools.
  3. Dynamic Tool Loading for Assistants:

    • The application would automatically gather all registered function schemas from the registry to provide the tools parameter when creating or running an Assistant. This eliminates the need to manually update this list.
  4. Dynamic Dispatch and Rendering in routers/chat.py:

    • When the Assistant requests a tool call (ThreadRunRequiresAction), routers/chat.py would:
      • Use tool_call.function.name to look up the corresponding function and its metadata in the registry.
      • Automatically parse and validate tool_call.function.arguments (potentially using the Pydantic model associated with the registered function).
      • Invoke the registered Python function with the parsed arguments.
      • Use the registered rendering information (e.g., template path) to generate the HTML output for the Server-Sent Event (SSE) that displays the tool's result.
      • This removes the need for hardcoded if/elif blocks for each function.

Developer Workflow with the New System:

To add a new custom function, a developer would typically:

  1. Define the function logic in a designated location (e.g., a specific file or module for custom tools).
  2. Define its input schema (e.g., using a Pydantic model).
  3. (Optional) Create an HTML template for its output if custom rendering is desired.
  4. Register the function with the central tool registry, providing its logic, schema, and rendering information.

This significantly reduces the need to modify routers/chat.py directly for each new function, centralizing the tool-specific logic and making the overall system more pluggable.

4. Next Steps

  • Discuss and refine this high-level design.
  • Explore specific implementation details for the registration mechanism (e.g., decorators, explicit registration calls).
  • Define how Pydantic models could be best utilized for argument parsing and schema generation.
  • Plan the refactoring of utils/custom_functions.py and routers/chat.py to implement this new system.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions