Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update dynamic flows to specify transition_callback within the function definition #84

Merged
merged 4 commits into from
Jan 22, 2025

Conversation

markbackman
Copy link
Contributor

In switching to a per function handler, we have the opportunity to mimic the static flow format of including the transition callback within the node itself. This change does exactly that; transition_callback is now defined within the function itself, matching the implementation for static flows.

Copy link

vercel bot commented Jan 22, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
pipecat-flows ✅ Ready (Inspect) Visit Preview Jan 22, 2025 6:19pm

@markbackman
Copy link
Contributor Author

markbackman commented Jan 22, 2025

@filipi87 I ended up making this change for the reasons I outlined in the description. What do you think of this solution vs the one on main that you approved yesterday, #82?

I'm hoping to make the FlowManager args simpler and co-locate node information in the node itself. But, I want to make sure this is a positive experience for developers.

@filipi87
Copy link

@filipi87 I ended up making this change for the reasons I outlined in the description. What do you think of this solution vs the one on main that you approved yesterday, #82?

I think both approaches are easy to understand and provide a good developer experience.

As you outlined here, this one has the advantage of mimicking the static flow format. So, I think it makes sense for us to proceed with this approach.

@filipi87
Copy link

Hi @markbackman,

This might not be directly related to this PR, but I wanted to mention it here in case it's worth discussing.

While reviewing our examples, I noticed that for each provider (OpenAI, Gemini, and Anthropic), the functions are declared differently inside the examples.

Because of this, we need to handle them differently in set_node and _remove_transition_info.

Shouldn't this logic be encapsulated within the adapters? Regardless of the provider being used, shouldn't we declare the functions in the same way ?

How we are currently declaring the functions for each provider in the examples:

OpenAI:

"functions": [
      {
          "type": "function",
          "function": {
              "name": "collect_marital_status",
              "handler": collect_marital_status,
              "description": "Record marital status",
              "parameters": {
                  "type": "object",
                  "properties": {
                      "marital_status": {"type": "string", "enum": ["single", "married"]}
                  },
                  "required": ["marital_status"],
              },
              "transition_callback": handle_marital_status_collection,
          },
      }
  ],

Gemini:

"functions": [
    {
        "function_declarations": [
            {
                "name": "collect_age",
                "handler": collect_age,
                "description": "Record customer's age",
                "parameters": {
                    "type": "object",
                    "properties": {"age": {"type": "integer"}},
                    "required": ["age"],
                },
                "transition_callback": handle_age_collection,
            }
        ]
    }
],

Anthropic:

"functions": [
    {
        "name": "collect_age",
        "handler": collect_age,
        "description": "Record customer's age after they provide it",
        "input_schema": {
            "type": "object",
            "properties": {"age": {"type": "integer"}},
            "required": ["age"],
        },
        "transition_callback": handle_age_collection,
    }
],

Copy link

@filipi87 filipi87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR looks great.

I’ve left a comment about establishing a standard for how we declare functions, regardless of the provider. If we think it’s worthwhile, we could consider implementing this in a follow-up PR. 🙂

@markbackman
Copy link
Contributor Author

markbackman commented Jan 22, 2025

While reviewing our examples, I noticed that for each provider (OpenAI, Gemini, and Anthropic), the functions are declared differently inside the examples.

You raise a good point. For messages, Pipecat has aligned with the OpenAI format, but for function calling, there are enough differences that Pipecat still requires the native function calling format. This has the inconvenient side effect of requiring these types of differences to be used.

I think this is really an issue we should take up in the core Pipecat code; that is, we should determine if we could standardize on a single format. When we last discussed (Q4 '24), we decided that there were still too many differences to handle.

@filipi87 given this, any issue with me merging this change?

@filipi87
Copy link

@filipi87 given this, any issue with me merging this change?

No issue at all. We can discuss the other topic completely independently of this PR.

@markbackman markbackman merged commit 036ec7e into main Jan 22, 2025
2 checks passed
@markbackman markbackman deleted the mb/transition-in-function branch January 22, 2025 23:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants