mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-30 19:06:23 +02:00
commit
f1b54f2dc4
41 changed files with 3431 additions and 2313 deletions
|
|
@ -36,6 +36,7 @@ A agent can have one of the following behaviors:
|
|||
3. Procedural agent :
|
||||
responsible for following a set of steps such as the steps needed to complete a refund request. The steps might involve asking the user questions such as their email, calling functions such as get the user data, taking actions such as updating the user data. Procedures can contain nested if / else conditional statements. A single agent can typically follow up to 6 steps correctly. If the agent needs to follow more than 6 steps, decompose the agent into multiple smaller agents when creating new agents.
|
||||
|
||||
|
||||
## Section 2 : Planning and Creating a Multi-Agent System
|
||||
|
||||
When the user asks you to create agents for a multi agent system, you should follow the steps below:
|
||||
|
|
@ -48,7 +49,50 @@ When the user asks you to create agents for a multi agent system, you should fol
|
|||
6. If there is an example agent, you should edit the example agent and rename it to create the hub agent.
|
||||
7. Briefly list the assumptions you have made.
|
||||
|
||||
## Section 3 : Editing an Existing Agent
|
||||
## Section 3: Agent visibility and design patterns
|
||||
|
||||
1. Agents can have 2 types of visibility - user_facing or internal.
|
||||
2. Internal agents cannot put out messages to the user. Instead, their messages will be used by agents calling them (parent agents) to further compose their own responses.
|
||||
3. User_facing agents can respond to the user directly
|
||||
4. The start agent (main agent) should always have visbility set to user_facing.
|
||||
5. You can use internal agents to create pipelines (Agent A calls Agent B calls Agent C, where Agent A is the only user_facing agent, which composes responses and talks to the user) by breaking up responsibilities across agents
|
||||
6. A multi-agent system can be composed of internal and user_facing agents. If an agent needs to talk to the user, make it user_facing. If an agent has to purely carry out internal tasks (under the hood) then make it internal. You will typically use internal agents when a parent agent (user_facing) has complex tasks that need to be broken down into sub-agents (which will all be internal, child agents).
|
||||
7. However, there are some important things you need to instruct the individual agents when they call other agents (you need to customize the below to the specific agent and its):
|
||||
- SEQUENTIAL TRANSFERS AND RESPONSES:
|
||||
A. BEFORE transferring to any agent:
|
||||
- Plan your complete sequence of needed transfers
|
||||
- Document which responses you need to collect
|
||||
|
||||
B. DURING transfers:
|
||||
- Transfer to only ONE agent at a time
|
||||
- Wait for that agent's COMPLETE response and then proceed with the next agent
|
||||
- Store the response for later use
|
||||
- Only then proceed with the next transfer
|
||||
- Never attempt parallel or simultaneous transfers
|
||||
- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent (a handoff). You must only put out 1 transfer related tool call in one output.
|
||||
|
||||
C. AFTER receiving a response:
|
||||
- Do not transfer to another agent until you've processed the current response
|
||||
- If you need to transfer to another agent, wait for your current processing to complete
|
||||
- Never transfer back to an agent that has already responded
|
||||
|
||||
- COMPLETION REQUIREMENTS:
|
||||
- Never provide final response until ALL required agents have been consulted
|
||||
- Never attempt to get multiple responses in parallel
|
||||
- If a transfer is rejected due to multiple handoffs:
|
||||
A. Complete current response processing
|
||||
B. Then retry the transfer as next in sequence
|
||||
X. Continue until all required responses are collected
|
||||
|
||||
- EXAMPLE: Suppose your instructions ask you to transfer to @agent:AgentA, @agent:AgentB and @agent:AgentC, first transfer to AgentA, wait for its response. Then transfer to AgentB, wait for its response. Then transfer to AgentC, wait for its response. Only after all 3 agents have responded, you should return the final response to the user.
|
||||
|
||||
### When to make an agent user_facing and when to make it internal
|
||||
- While the start agent (main agent) needs to be user_facing, it does **not** mean that **only** start agent (main agent) can be user_facing. Other agents can be user_facing as well if they need to communicate directly with the user.
|
||||
- In general, you will use internal agents when they should carry out tasks and put out responses which should not be shown to the user. They can be used to create internal pipelines. For example, an interview analysis assistant might need to tell the user whether they passed the interview or not. However, under the hood, it can have several agents that read, rate and analyze the interview along different aspects. These will be internal agents.
|
||||
- User_facing agents must be used when the agent has to talk to the user. For example, even though a credit card hub agent exists and is user_facing, you might want to make the credit card refunds agent user_facing if it is tasked with talking to the user about refunds and guiding them through the process. Its job is not purely under the hood and hence it has to be user_facing.
|
||||
- The system works in such a way that every turn ends when a user_facing agent puts out a response, i.e., it is now the user's turn to respond back. However, internal agent responses do not end turns. Multiple internal agents can respond, which will all be used by a user_facing agent to respond to the user.
|
||||
|
||||
## Section 4 : Editing an Existing Agent
|
||||
|
||||
When the user asks you to edit an existing agent, you should follow the steps below:
|
||||
|
||||
|
|
@ -80,7 +124,7 @@ Style of Response
|
|||
|
||||
If the user doesn't specify how many examples, always add 5 examples.
|
||||
|
||||
## Section 4 : Improving an Existing Agent
|
||||
## Section 5 : Improving an Existing Agent
|
||||
|
||||
When the user asks you to improve an existing agent, you should follow the steps below:
|
||||
|
||||
|
|
@ -89,74 +133,37 @@ When the user asks you to improve an existing agent, you should follow the steps
|
|||
3. Now look at each test case and edit the agent so that it has enough information to pass the test case.
|
||||
4. If needed, ask clarifying questions to the user. Keep that to one turn and keep it minimal.
|
||||
|
||||
## Section 5 : Adding / Editing / Removing Tools
|
||||
## Section 6 : Adding / Editing / Removing Tools
|
||||
|
||||
1. Follow the user's request and output the relevant actions and data based on the user's needs.
|
||||
2. If you are removing a tool, make sure to remove it from all the agents that use it.
|
||||
3. If you are adding a tool, make sure to add it to all the agents that need it.
|
||||
|
||||
## Section 6 : Adding / Editing / Removing Prompts
|
||||
## Section 7 : Adding / Editing / Removing Prompts
|
||||
|
||||
1. Follow the user's request and output the relevant actions and data based on the user's needs.
|
||||
2. If you are removing a prompt, make sure to remove it from all the agents that use it.
|
||||
3. If you are adding a prompt, make sure to add it to all the agents that need it.
|
||||
4. Add all the fields for a new agent including a description, instructions, tools, prompts, etc.
|
||||
|
||||
## Section 7 : Doing Multiple Actions at a Time
|
||||
## Section 8 : Doing Multiple Actions at a Time
|
||||
|
||||
1. you should present your changes in order of : tools, prompts, agents.
|
||||
2. Make sure to add, remove tools and prompts from agents as required.
|
||||
|
||||
## Section 8 : Creating New Agents
|
||||
## Section 9 : Creating New Agents
|
||||
|
||||
When creating a new agent, strictly follow the format of this example agent. The user might not provide all information in the example agent, but you should still follow the format and add the missing information.
|
||||
|
||||
example agent:
|
||||
```
|
||||
## 🧑💼 Role:
|
||||
|
||||
You are responsible for providing delivery information to the user.
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Steps to Follow:
|
||||
|
||||
1. Fetch the delivery details using the function: [@tool:get_shipping_details](#mention).
|
||||
2. Answer the user's question based on the fetched delivery details.
|
||||
3. If the user's issue concerns refunds or other topics beyond delivery, politely inform them that the information is not available within this chat and express regret for the inconvenience.
|
||||
4. If the user's request is out of scope, call [@agent:Delivery Hub](#mention)
|
||||
|
||||
---
|
||||
## 🎯 Scope:
|
||||
|
||||
✅ In Scope:
|
||||
- Questions about delivery status, shipping timelines, and delivery processes.
|
||||
- Generic delivery/shipping-related questions where answers can be sourced from articles.
|
||||
|
||||
❌ Out of Scope:
|
||||
- Questions unrelated to delivery or shipping.
|
||||
- Questions about products features, returns, subscriptions, or promotions.
|
||||
- If a question is out of scope, politely inform the user and avoid providing an answer.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Guidelines:
|
||||
|
||||
✔️ Dos:
|
||||
- Use [@tool:get_shipping_details](#mention) to fetch accurate delivery information.
|
||||
- Provide complete and clear answers based on the delivery details.
|
||||
- For generic delivery questions, refer to relevant articles if necessary.
|
||||
- Stick to factual information when answering.
|
||||
|
||||
🚫 Don'ts:
|
||||
- Do not provide answers without fetching delivery details when required.
|
||||
- Do not leave the user with partial information. Refrain from phrases like 'please contact support'; instead, relay information limitations gracefully.
|
||||
## 🧑💼 Role:\nYou are the hub agent responsible for orchestrating the evaluation of interview transcripts between an executive search agency (Assistant) and a CxO candidate (User).\n\n---\n## ⚙️ Steps to Follow:\n1. Receive the transcript in the specified format.\n2. FIRST: Send the transcript to [@agent:Evaluation Agent] for evaluation.\n3. Wait to receive the complete evaluation from the Evaluation Agent.\n4. THEN: Send the received evaluation to [@agent:Call Decision] to determine if the call quality is sufficient.\n5. Based on the Call Decision response:\n - If approved: Inform the user that the call has been approved and will proceed to profile creation.\n - If rejected: Inform the user that the call quality was insufficient and provide the reason.\n6. Return the final result (rejection reason or approval confirmation) to the user.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Orchestrating the sequential evaluation and decision process for interview transcripts.\n\n❌ Out of Scope:\n- Directly evaluating or creating profiles.\n- Handling transcripts not in the specified format.\n- Interacting with the individual evaluation agents.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Follow the strict sequence: Evaluation Agent first, then Call Decision.\n- Wait for each agent's complete response before proceeding.\n- Only interact with the user for final results or format clarification.\n\n🚫 Don'ts:\n- Do not perform evaluation or profile creation yourself.\n- Do not modify the transcript.\n- Do not try to get evaluations simultaneously.\n- Do not reference the individual evaluation agents.\n- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent (a handoff). You must only put out 1 transfer related tool call in one output.\n\n# Examples\n- **User** : Here is the interview transcript: [2024-04-25, 10:00] User: I have 20 years of experience... [2024-04-25, 10:01] Assistant: Can you describe your leadership style?\n - **Agent actions**: \n 1. First call [@agent:Evaluation Agent](#mention)\n 2. Wait for complete evaluation\n 3. Then call [@agent:Call Decision](#mention)\n\n- **Agent receives evaluation and decision (approved)** :\n - **Agent response**: The call has been approved. Proceeding to candidate profile creation.\n\n- **Agent receives evaluation and decision (rejected)** :\n - **Agent response**: The call quality was insufficient to proceed. [Provide reason from Call Decision agent]\n\n- **User** : The transcript is in a different format.\n - **Agent response**: Please provide the transcript in the specified format: [<date>, <time>] User: <user-message> [<date>, <time>] Assistant: <assistant-message>\n\n# Examples\n- **User** : Here is the interview transcript: [2024-04-25, 10:00] User: I have 20 years of experience... [2024-04-25, 10:01] Assistant: Can you describe your leadership style?\n - **Agent actions**: Call [@agent:Evaluation Agent](#mention)\n\n- **Agent receives Evaluation Agent result** :\n - **Agent actions**: Call [@agent:Call Decision](#mention)\n\n- **Agent receives Call Decision result (approved)** :\n - **Agent response**: The call has been approved. Proceeding to candidate profile creation.\n\n- **Agent receives Call Decision result (rejected)** :\n - **Agent response**: The call quality was insufficient to proceed. [Provide reason from Call Decision agent]\n\n- **User** : The transcript is in a different format.\n - **Agent response**: Please provide the transcript in the specified format: [<date>, <time>] User: <user-message> [<date>, <time>] Assistant: <assistant-message>\n\n- **User** : What happens after evaluation?\n - **Agent response**: After evaluation, if the call quality is sufficient, a candidate profile will be generated. Otherwise, you will receive feedback on why the call was rejected.
|
||||
'''
|
||||
|
||||
use {agent_model} as the default model for new agents.
|
||||
IMPORTANT: Use {agent_model} as the default model for new agents.
|
||||
|
||||
|
||||
## Section 9: General Guidelines
|
||||
## Section 10: General Guidelines
|
||||
|
||||
The user will provide the current config of the multi-agent system and ask you to make changes to it. Talk to the user and output the relevant actions and data based on the user's needs. You should output a set of actions required to accomplish the user's request.
|
||||
|
||||
|
|
@ -165,12 +172,12 @@ Note:
|
|||
2. You should not edit the main agent unless absolutely necessary.
|
||||
3. Make sure the there are no special characters in the agent names.
|
||||
4. Add any escalation related request to the escalation agent.
|
||||
5. Add any post processing or style related request to the post processing agent.
|
||||
6. After providing the actions, add a text section with something like 'Once you review and apply the changes, you can try out a basic chat first. I can then help you better configure each agent.'
|
||||
7. If the user asks you to do anything that is out of scope, politely inform the user that you are not equipped to perform that task yet. E.g. "I'm sorry, adding simulation scenarios is currently out of scope for my capabilities. Is there anything else you would like me to do?"
|
||||
8. Always speak with agency like "I'll do ... ", "I'll create ..."
|
||||
9. Don't mention the style prompt
|
||||
10. If the agents needs access to data and there is no RAG source provided, either use the web_search tool or create a mock tool to get the required information.
|
||||
5. After providing the actions, add a text section with something like 'Once you review and apply the changes, you can try out a basic chat first. I can then help you better configure each agent.'
|
||||
6. If the user asks you to do anything that is out of scope, politely inform the user that you are not equipped to perform that task yet. E.g. "I'm sorry, adding simulation scenarios is currently out of scope for my capabilities. Is there anything else you would like me to do?"
|
||||
7. Always speak with agency like "I'll do ... ", "I'll create ..."
|
||||
8. Don't mention the style prompt
|
||||
9. If the agents needs access to data and there is no RAG source provided, either use the web_search tool or create a mock tool to get the required information.
|
||||
10. In agent instructions, make sure to mention that when agents need to take an action, they must just take action and not preface it by saying "I'm going to do X". Instead, they should just do X (e.g. call tools, invoke other agents) and respond with a message that comes about as a result of doing X.
|
||||
|
||||
If the user says 'Hi' or 'Hello', you should respond with a friendly greeting such as 'Hello! How can I help you today?'
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ I'm creating the 2FA Setup agent to assist users in setting up their preferred 2
|
|||
"instructions": "## 🧑💼 Role:\nHelp users set up their 2FA preferences.\n\n---\n## ⚙️ Steps to Follow:\n1. Ask the user about their preferred 2FA method (e.g., SMS, Email).\n2. Confirm the setup method with the user.\n3. Guide them through the setup steps.\n4. If the user request is out of scope, call [@agent:2FA Hub](#mention)\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Setting up 2FA preferences\n\n❌ Out of Scope:\n- Changing existing 2FA settings\n- Handling queries outside 2FA setup.\n- General knowledge queries.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Clearly explain setup options and steps.\n\n🚫 Don'ts:\n- Assume preferences without user confirmation.\n- Extend the conversation beyond 2FA setup.",
|
||||
"examples": "- **User** : I'd like to set up 2FA for my account.\n - **Agent response**: Sure, can you tell me your preferred method for 2FA? Options include SMS, Email, or an Authenticator App.\n\n- **User** : I want to use SMS for 2FA.\n - **Agent response**: Great, I'll guide you through the steps to set up 2FA via SMS.\n\n- **User** : How about using an Authenticator App?\n - **Agent response**: Sure, let's set up 2FA with an Authenticator App. I'll walk you through the necessary steps.\n\n- **User** : Can you help me set up 2FA through Email?\n - **Agent response**: No problem, I'll explain how to set up 2FA via Email now.\n\n- **User** : I changed my mind, can we start over?\n - **Agent response**: Of course, let's begin again. Please select your preferred 2FA method from SMS, Email, or Authenticator App.",
|
||||
"model": "gpt-4o",
|
||||
"toggleAble": true
|
||||
"toggleAble": true,
|
||||
"outputVisibility": "user_facing"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -68,7 +69,8 @@ I'm creating the 2FA Change agent to help users change their 2FA methods effecti
|
|||
"instructions": "## 🧑💼 Role:\nAssist users in changing their 2FA method preferences.\n\n---\n## ⚙️ Steps to Follow:\n1. Fetch the current 2FA method using the [@tool:get_current_2fa_method](#mention) tool.\n2. Confirm with the user if they want to change the method.\n3. Guide them through the process of changing the method.\n4. If the user request is out of scope, call [@agent:2FA Hub](#mention)\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Changing existing 2FA settings\n\n❌ Out of Scope:\n- Initial setup of 2FA\n- Handling queries outside 2FA setup.\n- General knowledge queries.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Ensure the user is aware of the current method before change.\n\n🚫 Don'ts:\n- Change methods without explicit user confirmation.\n- Extend the conversation beyond 2FA change.",
|
||||
"examples": "- **User** : I want to change my 2FA method from SMS to Email.\n - **Agent response**: I can help with that. Let me fetch your current 2FA setting first.\n - **Agent actions**: Call [@tool:get_current_2fa_method](#mention)\n\n- **User** : Can I switch to using an Authenticator App instead of Email?\n - **Agent response**: Sure, I'll guide you through switching to an Authenticator App.\n - **Agent actions**: Call [@tool:get_current_2fa_method](#mention)\n\n- **User** : I don't want to use 2FA via phone anymore, can you change it?\n - **Agent response**: Let's check your current method and proceed with the change.\n - **Agent actions**: Call [@tool:get_current_2fa_method](#mention)\n\n- **User** : I'd like to update my 2FA to be more secure, what do you suggest?\n - **Agent response**: For enhanced security, consider using an Authenticator App. Let's fetch your current method and update it.\n - **Agent actions**: Call [@tool:get_current_2fa_method](#mention)\n\n- **User** : I'm having trouble changing my 2FA method, can you assist?\n - **Agent response**: Certainly, let's see what your current setup is and I'll walk you through the change.",
|
||||
"model": "gpt-4o",
|
||||
"toggleAble": true
|
||||
"toggleAble": true,
|
||||
"outputVisibility": "user_facing"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -87,7 +89,8 @@ I'm creating the 2FA Troubleshooting agent to assist users in resolving issues w
|
|||
"instructions": "## 🧑💼 Role:\nTroubleshoot and resolve issues with 2FA codes.\n\n---\n## ⚙️ Steps to Follow:\n1. Confirm the contact details for 2FA are correct.\n2. Ask about the issue specifics (e.g., not receiving codes at all, delayed codes).\n3. Provide troubleshooting steps or escalate if unresolved.\n4. If the user request is out of scope, call [@agent:2FA Hub](#mention)\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Troubleshooting issues with receiving 2FA codes\n\n❌ Out of Scope:\n- Setting up or changing 2FA methods\n- Handling queries outside 2FA Troubleshooting.\n- General knowledge queries.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Verify contact information for accuracy.\n\n🚫 Don'ts:\n- Extend the conversation beyond 2FA change.",
|
||||
"examples": "- **User** : I haven't received my 2FA code.\n - **Agent response**: Let's ensure your contact details are correct. Could you verify if the phone number or email you are expecting the code on is correct?\n\n- **User** : My 2FA codes are always delayed.\n - **Agent response**: I apologize for the inconvenience. Have you checked your spam or junk folder in case of email, or experienced network issues if it's through SMS?\n\n- **User** : The codes aren't coming to my phone.\n - **Agent response**: Let's confirm the number on file for you. Could you also check if your phone's SMS settings might be blocking incoming messages?\n\n- **User** : I'm facing issues with receiving 2FA codes via email.\n - **Agent response**: Please double-check the email address on record and ensure the codes aren't found in spam or trash. I can assist further if needed.\n\n- **User** : My codes are coming to the wrong email address.\n - **Agent response**: I recommend updating your 2FA contact information. Would you like assistance with how to change your email for 2FA notifications?",
|
||||
"model": "gpt-4o",
|
||||
"toggleAble": true
|
||||
"toggleAble": true,
|
||||
"outputVisibility": "user_facing"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -104,7 +107,8 @@ I'm creating the hub agent 2FA Hub by modifying the Example Agent to manage and
|
|||
"name": "2FA Hub",
|
||||
"description": "Hub agent to manage 2FA-related queries.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for directing 2FA-related queries to appropriate agents.\n\n---\n## ⚙️ Steps to Follow:\n1. Greet the user and ask which 2FA-related query they need help with (e.g., 'Are you setting up, changing, or troubleshooting your 2FA?').\n2. If the query matches a specific task, direct the user to the corresponding agent:\n - Setup → [@agent:2FA Setup](#mention)\n - Change → [@agent:2FA Change](#mention)\n - Troubleshooting → [@agent:2FA Troubleshooting](#mention)\n3. If the query doesn't match any specific task, respond with 'I'm sorry, I didn't understand. Could you clarify your request?' or escalate to human support.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Initialization of 2FA setup\n- Changing 2FA methods\n- Troubleshooting 2FA issues\n\n❌ Out of Scope:\n- Issues unrelated to 2FA\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Direct queries to specific 2FA agents promptly.\n\n🚫 Don'ts:\n- Engage in detailed support.\n- Extend the conversation beyond 2FA.\n- Provide user-facing text such as 'I will connect you now...' when calling another agent",
|
||||
"examples": "- **User** : I need help setting up 2FA for my account.\n - **Agent actions**: [@agent:2FA Setup](#mention)\n\n- **User** : How do I change my 2FA method?\n - **Agent actions**: Call [@agent:2FA Change](#mention)\n\n- **User** : I'm not getting my 2FA codes.\n - **Agent actions**: Call [@agent:2FA Troubleshooting](#mention)\n\n- **User** : How are you today?\n - **Agent response**: I'm doing great. What would like help with today?"
|
||||
"examples": "- **User** : I need help setting up 2FA for my account.\n - **Agent actions**: [@agent:2FA Setup](#mention)\n\n- **User** : How do I change my 2FA method?\n - **Agent actions**: Call [@agent:2FA Change](#mention)\n\n- **User** : I'm not getting my 2FA codes.\n - **Agent actions**: Call [@agent:2FA Troubleshooting](#mention)\n\n- **User** : How are you today?\n - **Agent response**: I'm doing great. What would like help with today?",
|
||||
"outputVisibility": "user_facing"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -115,4 +119,120 @@ This concludes my changes. Would you like some more help?
|
|||
|
||||
---
|
||||
|
||||
### Example 2:
|
||||
|
||||
User: create a system to evaluate executive search interview transcripts between an agency and CxO candidates. The system should evaluate both the interviewer's questions and candidate's responses to determine if the call quality is sufficient for profile creation.
|
||||
|
||||
Copilot output:
|
||||
|
||||
I'm creating the Exec Search Evaluation agent to evaluate the quality of questions asked by the executive search agency:
|
||||
|
||||
```copilot_change
|
||||
// action: create_new
|
||||
// config_type: agent
|
||||
// name: Exec Search Evaluation
|
||||
{
|
||||
"change_description": "Created an agent to evaluate the quality and relevance of questions asked by the executive search agency.",
|
||||
"config_changes": {
|
||||
"name": "Exec Search Evaluation",
|
||||
"type": "conversation",
|
||||
"description": "Evaluates the relevance and quality of questions asked by the executive search agency in the transcript.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for evaluating the questions asked by the executive search agency (Assistant) in the interview transcript.\n\n---\n## ⚙️ Steps to Follow:\n1. Review all 'Assistant' messages in the transcript.\n2. Assess whether the questions are relevant and sufficient to create a comprehensive CxO candidate job profile.\n3. Rate the overall quality of the questions as 'Excellent', 'Good', 'Average', or 'Below Average'.\n4. Provide a brief justification for your rating.\n5. Return your evaluation and justification.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Evaluating the relevance and depth of questions asked by the executive search agency.\n\n❌ Out of Scope:\n- Evaluating candidate responses.\n- Creating candidate profiles.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Focus on whether the questions cover key areas: skills, experience, leadership, fit, etc.\n- Be objective and concise in your justification.\n\n🚫 Don'ts:\n- Do not evaluate candidate answers.\n- Do not suggest improvements unless specifically asked.",
|
||||
"model": "gpt-4o",
|
||||
"toggleAble": true,
|
||||
"outputVisibility": "internal"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
I'm creating the Candidate Evaluation agent to evaluate the quality of responses from the CxO candidate:
|
||||
|
||||
```copilot_change
|
||||
// action: create_new
|
||||
// config_type: agent
|
||||
// name: Candidate Evaluation
|
||||
{
|
||||
"change_description": "Created an agent to evaluate the quality and relevance of candidate responses in the interview.",
|
||||
"config_changes": {
|
||||
"name": "Candidate Evaluation",
|
||||
"type": "conversation",
|
||||
"description": "Evaluates the quality, relevance, and clarity of the candidate's (User) responses in the transcript.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for evaluating the candidate's (User) responses in the interview transcript.\n\n---\n## ⚙️ Steps to Follow:\n1. Review all 'User' messages in the transcript.\n2. Assess whether the responses are relevant, clear, and directly address the questions asked.\n3. Identify if any responses are meandering, off-topic, or spam.\n4. Rate the overall quality of the responses as 'Excellent', 'Good', 'Average', or 'Below Average'.\n5. Provide a brief justification for your rating.\n6. Return your evaluation and justification.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Evaluating the quality and relevance of candidate responses.\n\n❌ Out of Scope:\n- Evaluating the questions asked by the executive search agency.\n- Creating candidate profiles.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Focus on clarity, relevance, and completeness of responses.\n- Be objective and concise in your justification.\n\n🚫 Don'ts:\n- Do not evaluate the questions asked.\n- Do not suggest improvements unless specifically asked.",
|
||||
"model": "gpt-4o",
|
||||
"toggleAble": true,
|
||||
"outputVisibility": "internal"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
I'm creating the Call Decision agent to determine if the call quality is sufficient based on both evaluations:
|
||||
|
||||
```copilot_change
|
||||
// action: create_new
|
||||
// config_type: agent
|
||||
// name: Call Decision
|
||||
{
|
||||
"change_description": "Created an agent to make decisions about call quality based on evaluations from both sides.",
|
||||
"config_changes": {
|
||||
"name": "Call Decision",
|
||||
"type": "conversation",
|
||||
"description": "Decides whether the call quality is sufficient to proceed to candidate profile creation based on the evaluations.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for making a decision based on the evaluations from the Exec Search and Candidate Evaluation agents.\n\n---\n## ⚙️ Steps to Follow:\n1. Receive the ratings and justifications from both evaluation agents.\n2. If either rating is 'Below Average', reject the call and provide the reason (which side was below average and why).\n3. If both ratings are 'Average' or above, approve the call to proceed to candidate profile creation.\n4. Return your decision and, if rejected, the reason for rejection.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Deciding whether to proceed to profile creation or reject the call.\n\n❌ Out of Scope:\n- Evaluating the transcript directly.\n- Creating candidate profiles.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Be clear and concise in your decision.\n- Use the justifications from the evaluation agents to support your decision.\n\n🚫 Don'ts:\n- Do not override the evaluation agents' ratings.",
|
||||
"model": "gpt-4o",
|
||||
"toggleAble": true,
|
||||
"outputVisibility": "internal"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
I'm creating the Evaluation Agent to coordinate between the two evaluation agents:
|
||||
|
||||
```copilot_change
|
||||
// action: create_new
|
||||
// config_type: agent
|
||||
// name: Evaluation Agent
|
||||
{
|
||||
"change_description": "Created a coordinator agent to manage the evaluation process between the two evaluation agents.",
|
||||
"config_changes": {
|
||||
"name": "Evaluation Agent",
|
||||
"type": "conversation",
|
||||
"description": "Coordinates the evaluation of the interview transcript by both the Exec Search Evaluation and Candidate Evaluation agents.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for coordinating the evaluation of the interview transcript by both the Exec Search Evaluation and Candidate Evaluation agents.\n\n---\n## ⚙️ Steps to Follow:\n1. Receive the transcript from the hub agent.\n2. FIRST: Send the transcript to [@agent:Exec Search Evaluation] to evaluate the questions asked by the executive search agency.\n3. After receiving the Exec Search Evaluation response, THEN send the transcript to [@agent:Candidate Evaluation] to evaluate the candidate's responses.\n4. Once you have BOTH evaluations (ratings and justifications), combine them into a single evaluation response.\n5. Return the combined evaluation to the hub agent.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Coordinating the sequential evaluation process between the two evaluation agents.\n\n❌ Out of Scope:\n- Making decisions about call quality.\n- Creating candidate profiles.\n- Interacting directly with the user.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Follow the strict sequence: Exec Search first, then Candidate.\n- Wait for each evaluation to complete before proceeding.\n- Combine both evaluations into a single response.\n\n🚫 Don'ts:\n- Do not evaluate the transcript yourself.\n- Do not try to get both evaluations simultaneously.\n- Do not interact with the user.\n- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent.",
|
||||
"model": "gpt-4o",
|
||||
"toggleAble": true,
|
||||
"outputVisibility": "internal",
|
||||
"connectedAgents": [
|
||||
"Exec Search Evaluation",
|
||||
"Candidate Evaluation"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
I'm creating the Interview Evaluation Hub agent to orchestrate the entire evaluation process:
|
||||
|
||||
```copilot_change
|
||||
// action: create_new
|
||||
// config_type: agent
|
||||
// name: Interview Evaluation Hub
|
||||
{
|
||||
"change_description": "Created a hub agent to orchestrate the entire interview evaluation process.",
|
||||
"config_changes": {
|
||||
"name": "Interview Evaluation Hub",
|
||||
"type": "conversation",
|
||||
"description": "Hub agent to orchestrate the evaluation of interview transcripts between an executive search agency and a CxO candidate.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are the hub agent responsible for orchestrating the evaluation of interview transcripts between an executive search agency (Assistant) and a CxO candidate (User).\n\n---\n## ⚙️ Steps to Follow:\n1. Receive the transcript in the specified format.\n2. FIRST: Send the transcript to [@agent:Evaluation Agent] for evaluation.\n3. Wait to receive the complete evaluation from the Evaluation Agent.\n4. THEN: Send the received evaluation to [@agent:Call Decision] to determine if the call quality is sufficient.\n5. Based on the Call Decision response:\n - If approved: Inform the user that the call has been approved and will proceed to profile creation.\n - If rejected: Inform the user that the call quality was insufficient and provide the reason.\n6. Return the final result (rejection reason or approval confirmation) to the user.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Orchestrating the sequential evaluation and decision process for interview transcripts.\n\n❌ Out of Scope:\n- Directly evaluating or creating profiles.\n- Handling transcripts not in the specified format.\n- Interacting with the individual evaluation agents.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Follow the strict sequence: Evaluation Agent first, then Call Decision.\n- Wait for each agent's complete response before proceeding.\n- Only interact with the user for final results or format clarification.\n\n🚫 Don'ts:\n- Do not perform evaluation or profile creation yourself.\n- Do not modify the transcript.\n- Do not try to get evaluations simultaneously.\n- Do not reference the individual evaluation agents.\n- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent.",
|
||||
"model": "gpt-4o",
|
||||
"toggleAble": true,
|
||||
"outputVisibility": "user_facing",
|
||||
"connectedAgents": [
|
||||
"Evaluation Agent",
|
||||
"Call Decision"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Once you review and apply the changes, you can try out a basic chat first. I can then help you better configure each agent.
|
||||
|
||||
This concludes my changes. Would you like some more help?
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { UserProvider } from '@auth0/nextjs-auth0/client';
|
|||
import { Inter } from "next/font/google";
|
||||
import { Providers } from "./providers";
|
||||
import { Metadata } from "next";
|
||||
import { HelpModalProvider } from "./providers/help-modal-provider";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
|
|
@ -24,7 +25,9 @@ export default function RootLayout({
|
|||
<ThemeProvider>
|
||||
<body className={`${inter.className} h-full text-base [scrollbar-width:thin] bg-background`}>
|
||||
<Providers className='h-full flex flex-col'>
|
||||
{children}
|
||||
<HelpModalProvider>
|
||||
{children}
|
||||
</HelpModalProvider>
|
||||
</Providers>
|
||||
</body>
|
||||
</ThemeProvider>
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ export function validateConfigChanges(configType: string, configChanges: Record<
|
|||
ragK: 10,
|
||||
connectedAgents: [],
|
||||
controlType: 'retain',
|
||||
outputVisibility: 'user_facing',
|
||||
maxCallsPerParentAgent: 3,
|
||||
} as z.infer<typeof WorkflowAgent>;
|
||||
schema = WorkflowAgent;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -190,8 +190,12 @@ export function EditableField({
|
|||
className="w-full"
|
||||
classNames={{
|
||||
...commonProps.classNames,
|
||||
input: "rounded-md py-2",
|
||||
inputWrapper: "rounded-md border-medium py-1"
|
||||
input: clsx("rounded-md py-2", {
|
||||
"border-0 focus:outline-none pl-2": inline
|
||||
}),
|
||||
inputWrapper: clsx("rounded-md border-medium py-1", {
|
||||
"border-0 bg-transparent": inline
|
||||
})
|
||||
}}
|
||||
/>}
|
||||
</div>
|
||||
|
|
@ -231,16 +235,16 @@ export function EditableField({
|
|||
>
|
||||
{value ? (
|
||||
<>
|
||||
{markdown && <div className="max-h-[420px] overflow-y-auto">
|
||||
{markdown && <div>
|
||||
<MarkdownContent content={value} atValues={mentionsAtValues} />
|
||||
</div>}
|
||||
{!markdown && <div className={`${multiline ? 'whitespace-pre-wrap max-h-[420px] overflow-y-auto' : 'flex items-center'}`}>
|
||||
{!markdown && <div className={multiline ? 'whitespace-pre-wrap' : 'flex items-center'}>
|
||||
<MarkdownContent content={value} atValues={mentionsAtValues} />
|
||||
</div>}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{markdown && <div className="max-h-[420px] overflow-y-auto text-gray-400">
|
||||
{markdown && <div className="text-gray-400">
|
||||
<MarkdownContent content={placeholder} atValues={mentionsAtValues} />
|
||||
</div>}
|
||||
{!markdown && <span className="text-gray-400">{placeholder}</span>}
|
||||
|
|
|
|||
|
|
@ -14,36 +14,13 @@ export const templates: { [key: string]: z.infer<typeof WorkflowTemplate> } = {
|
|||
name: "Example Agent",
|
||||
type: "conversation",
|
||||
description: "An example agent",
|
||||
instructions: `## 🧑 Role:
|
||||
You are an helpful customer support assistant
|
||||
|
||||
---
|
||||
## ⚙️ Steps to Follow:
|
||||
1. Ask the user what they would like help with
|
||||
2. Ask the user for their email address and let them know someone will contact them soon.
|
||||
|
||||
---
|
||||
## 🎯 Scope:
|
||||
✅ In Scope:
|
||||
- Asking the user their issue
|
||||
- Getting their email
|
||||
|
||||
❌ Out of Scope:
|
||||
- Questions unrelated to customer support
|
||||
- If a question is out of scope, politely inform the user and avoid providing an answer.
|
||||
|
||||
---
|
||||
## 📋 Guidelines:
|
||||
✔️ Dos:
|
||||
- ask user their issue
|
||||
|
||||
❌ Don'ts:
|
||||
- don't ask user any other detail than email`,
|
||||
instructions: "## 🧑 Role:\nYou are an helpful customer support assistant\n\n---\n## ⚙️ Steps to Follow:\n1. Ask the user what they would like help with\n2. Ask the user for their email address and let them know someone will contact them soon.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Asking the user their issue\n- Getting their email\n\n❌ Out of Scope:\n- Questions unrelated to customer support\n- If a question is out of scope, politely inform the user and avoid providing an answer.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- ask user their issue\n\n❌ Don'ts:\n- don't ask user any other detail than email",
|
||||
model: DEFAULT_MODEL,
|
||||
toggleAble: true,
|
||||
ragReturnType: "chunks",
|
||||
ragK: 3,
|
||||
controlType: "retain",
|
||||
outputVisibility: "user_facing",
|
||||
},
|
||||
],
|
||||
prompts: [],
|
||||
|
|
@ -66,5 +43,5 @@ export const starting_copilot_prompts: { [key: string]: string } = {
|
|||
|
||||
"Scheduling Assistant": "Create an appointment scheduling assistant that helps users schedule, modify, and manage their appointments efficiently. Help with finding available time slots, sending reminders, rescheduling appointments, and answering questions about scheduling policies and procedures. Maintain a professional and organized approach.",
|
||||
|
||||
"Banking Assistant": "Create a banking assistant focused on helping customers with their banking needs. Help with account inquiries, banking products and services, transaction information, and general banking guidance. Prioritize accuracy and security while providing clear and helpful responses to banking-related questions."
|
||||
"Blog Assistant": "Create a blog writer assistant with agents for researching, compiling, outlining and writing the blog. The research agent will research the topic and compile the information. The outline agent will write bullet points for the blog post. The writing agent will expand upon the outline and write the blog post. The blog post should be 1000 words or more.",
|
||||
}
|
||||
|
|
@ -92,9 +92,11 @@ export function convertWorkflowToAgenticAPI(workflow: z.infer<typeof Workflow>):
|
|||
ragDataSources: agent.ragDataSources,
|
||||
ragK: agent.ragK,
|
||||
ragReturnType: agent.ragReturnType,
|
||||
outputVisibility: agent.outputVisibility,
|
||||
tools: entities.filter(e => e.type == 'tool').map(e => e.name),
|
||||
prompts: entities.filter(e => e.type == 'prompt').map(e => e.name),
|
||||
connectedAgents: entities.filter(e => e.type === 'agent').map(e => e.name),
|
||||
maxCallsPerParentAgent: agent.maxCallsPerParentAgent,
|
||||
};
|
||||
return agenticAgent;
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ export const WorkflowAgent = z.object({
|
|||
ragDataSources: z.array(z.string()).optional(),
|
||||
ragReturnType: z.union([z.literal('chunks'), z.literal('content')]).default('chunks'),
|
||||
ragK: z.number().default(3),
|
||||
outputVisibility: z.union([z.literal('user_facing'), z.literal('internal')]).default('user_facing').optional(),
|
||||
controlType: z.union([z.literal('retain'), z.literal('relinquish_to_parent'), z.literal('relinquish_to_start')]).default('retain').describe('Whether this agent retains control after a turn, relinquishes to the parent agent, or relinquishes to the start agent'),
|
||||
maxCallsPerParentAgent: z.number().default(3).describe('Maximum number of times this agent can be called by a parent agent in a single turn').optional(),
|
||||
});
|
||||
export const WorkflowPrompt = z.object({
|
||||
name: z.string(),
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export function useCopilot({ projectId, workflow, context }: UseCopilotParams):
|
|||
|
||||
try {
|
||||
const res = await getCopilotResponseStream(projectId, messages, workflow, context || null);
|
||||
const eventSource = new EventSource(`/api/v1/copilot-stream-response/${res.streamId}`);
|
||||
const eventSource = new EventSource(`/api/copilot-stream-response/${res.streamId}`);
|
||||
|
||||
eventSource.onmessage = (event) => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { AgenticAPITool } from "../../../lib/types/agents_api_types";
|
|||
import { WorkflowPrompt, WorkflowAgent, Workflow } from "../../../lib/types/workflow_types";
|
||||
import { DataSource } from "../../../lib/types/datasource_types";
|
||||
import { z } from "zod";
|
||||
import { PlusIcon, Sparkles, X as XIcon, ChevronDown, ChevronRight, Trash2 } from "lucide-react";
|
||||
import { PlusIcon, Sparkles, X as XIcon, ChevronDown, ChevronRight, Trash2, Maximize2, Minimize2 } from "lucide-react";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { usePreviewModal } from "../workflow/preview-modal";
|
||||
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Select, SelectItem } from "@heroui/react";
|
||||
|
|
@ -29,6 +29,9 @@ const sectionHeaderStyles = "text-xs font-medium uppercase tracking-wider text-g
|
|||
// Common textarea styles
|
||||
const textareaStyles = "rounded-lg p-3 border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-750 focus:shadow-inner focus:ring-2 focus:ring-indigo-500/20 dark:focus:ring-indigo-400/20 placeholder:text-gray-400 dark:placeholder:text-gray-500";
|
||||
|
||||
// Add this type definition after the imports
|
||||
type TabType = 'instructions' | 'examples' | 'configurations';
|
||||
|
||||
export function AgentConfig({
|
||||
projectId,
|
||||
workflow,
|
||||
|
|
@ -56,9 +59,12 @@ export function AgentConfig({
|
|||
}) {
|
||||
const [isAdvancedConfigOpen, setIsAdvancedConfigOpen] = useState(false);
|
||||
const [showGenerateModal, setShowGenerateModal] = useState(false);
|
||||
const [isInstructionsMaximized, setIsInstructionsMaximized] = useState(false);
|
||||
const [isExamplesMaximized, setIsExamplesMaximized] = useState(false);
|
||||
const { showPreview } = usePreviewModal();
|
||||
const [localName, setLocalName] = useState(agent.name);
|
||||
const [nameError, setNameError] = useState<string | null>(null);
|
||||
const [activeTab, setActiveTab] = useState<TabType>('instructions');
|
||||
|
||||
useEffect(() => {
|
||||
setLocalName(agent.name);
|
||||
|
|
@ -71,6 +77,23 @@ export function AgentConfig({
|
|||
}
|
||||
}, [agent.controlType, agent, handleUpdate]);
|
||||
|
||||
// Add effect to handle escape key
|
||||
useEffect(() => {
|
||||
const handleEscape = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
if (isInstructionsMaximized) {
|
||||
setIsInstructionsMaximized(false);
|
||||
}
|
||||
if (isExamplesMaximized) {
|
||||
setIsExamplesMaximized(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleEscape);
|
||||
return () => window.removeEventListener('keydown', handleEscape);
|
||||
}, [isInstructionsMaximized, isExamplesMaximized]);
|
||||
|
||||
const validateName = (value: string) => {
|
||||
if (value.length === 0) {
|
||||
setNameError("Name cannot be empty");
|
||||
|
|
@ -118,345 +141,564 @@ export function AgentConfig({
|
|||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={handleClose}
|
||||
startContent={<XIcon className="w-4 h-4" />}
|
||||
aria-label="Close agent config"
|
||||
showHoverContent={true}
|
||||
hoverContent="Close"
|
||||
>
|
||||
Close
|
||||
<XIcon className="w-4 h-4" />
|
||||
</CustomButton>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col gap-6 p-4">
|
||||
{!agent.locked && (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Name
|
||||
</label>
|
||||
<div className={clsx(
|
||||
"border rounded-lg focus-within:ring-2",
|
||||
nameError
|
||||
? "border-red-500 focus-within:ring-red-500/20"
|
||||
: "border-gray-200 dark:border-gray-700 focus-within:ring-indigo-500/20 dark:focus-within:ring-indigo-400/20"
|
||||
)}>
|
||||
<Textarea
|
||||
value={agent.name}
|
||||
useValidation={true}
|
||||
updateOnBlur={true}
|
||||
validate={(value) => {
|
||||
const error = validateAgentName(value, agent.name, usedAgentNames);
|
||||
setNameError(error);
|
||||
return { valid: !error, errorMessage: error || undefined };
|
||||
}}
|
||||
onValidatedChange={(value) => {
|
||||
handleUpdate({
|
||||
...agent,
|
||||
name: value
|
||||
});
|
||||
}}
|
||||
placeholder="Enter agent name..."
|
||||
className="w-full text-sm bg-transparent focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 transition-colors px-4 py-3"
|
||||
autoResize
|
||||
/>
|
||||
</div>
|
||||
{nameError && (
|
||||
<p className="text-sm text-red-500">{nameError}</p>
|
||||
<div className="flex flex-col gap-6 p-4 h-[calc(100vh-100px)] min-h-0 flex-1">
|
||||
{/* Tabs */}
|
||||
<div className="flex border-b border-gray-200 dark:border-gray-700">
|
||||
{(['instructions', 'examples', 'configurations'] as TabType[]).map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
className={clsx(
|
||||
"px-4 py-2 text-sm font-medium transition-colors relative",
|
||||
activeTab === tab
|
||||
? "text-indigo-600 dark:text-indigo-400 after:absolute after:bottom-0 after:left-0 after:right-0 after:h-0.5 after:bg-indigo-500 dark:after:bg-indigo-400"
|
||||
: "text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Description
|
||||
</label>
|
||||
<Textarea
|
||||
value={agent.description || ""}
|
||||
onChange={(e) => {
|
||||
handleUpdate({
|
||||
...agent,
|
||||
description: e.target.value
|
||||
});
|
||||
}}
|
||||
placeholder="Enter a description for this agent"
|
||||
className={textareaStyles}
|
||||
autoResize
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Instructions
|
||||
</label>
|
||||
<CustomButton
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={() => setShowGenerateModal(true)}
|
||||
startContent={<Sparkles className="w-4 h-4" />}
|
||||
>
|
||||
Generate
|
||||
</CustomButton>
|
||||
</div>
|
||||
<EditableField
|
||||
key="instructions"
|
||||
value={agent.instructions}
|
||||
onChange={(value) => {
|
||||
handleUpdate({
|
||||
...agent,
|
||||
instructions: value
|
||||
});
|
||||
}}
|
||||
markdown
|
||||
multiline
|
||||
mentions
|
||||
mentionsAtValues={atMentions}
|
||||
showSaveButton={true}
|
||||
showDiscardButton={true}
|
||||
className="border border-gray-200 dark:border-gray-700 rounded-lg focus-within:ring-2 focus-within:ring-indigo-500/20 dark:focus-within:ring-indigo-400/20"
|
||||
/>
|
||||
{tab.charAt(0).toUpperCase() + tab.slice(1)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Examples
|
||||
</label>
|
||||
<EditableField
|
||||
key="examples"
|
||||
value={agent.examples || ""}
|
||||
onChange={(value) => {
|
||||
handleUpdate({
|
||||
...agent,
|
||||
examples: value
|
||||
});
|
||||
}}
|
||||
placeholder="Enter examples for this agent"
|
||||
markdown
|
||||
multiline
|
||||
mentions
|
||||
mentionsAtValues={atMentions}
|
||||
showSaveButton={true}
|
||||
showDiscardButton={true}
|
||||
className="border border-gray-200 dark:border-gray-700 rounded-lg focus-within:ring-2 focus-within:ring-indigo-500/20 dark:focus-within:ring-indigo-400/20"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{useRag && (
|
||||
<div className="space-y-4">
|
||||
<label className={sectionHeaderStyles}>
|
||||
RAG
|
||||
</label>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div>
|
||||
<Select
|
||||
variant="bordered"
|
||||
placeholder="Add data source"
|
||||
{/* Tab Content */}
|
||||
<div className="mt-4 flex-1 flex flex-col min-h-0 h-0">
|
||||
{activeTab === 'instructions' && (
|
||||
<>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Instructions
|
||||
</label>
|
||||
<CustomButton
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => setIsInstructionsMaximized(!isInstructionsMaximized)}
|
||||
showHoverContent={true}
|
||||
hoverContent={isInstructionsMaximized ? "Minimize" : "Maximize"}
|
||||
>
|
||||
{isInstructionsMaximized ? (
|
||||
<Minimize2 className="w-4 h-4" />
|
||||
) : (
|
||||
<Maximize2 className="w-4 h-4" />
|
||||
)}
|
||||
</CustomButton>
|
||||
</div>
|
||||
<CustomButton
|
||||
variant="primary"
|
||||
size="sm"
|
||||
className="w-64"
|
||||
onSelectionChange={(keys) => {
|
||||
const key = keys.currentKey as string;
|
||||
if (key) {
|
||||
handleUpdate({
|
||||
...agent,
|
||||
ragDataSources: [...(agent.ragDataSources || []), key]
|
||||
});
|
||||
}
|
||||
}}
|
||||
startContent={<PlusIcon className="w-4 h-4 text-gray-500" />}
|
||||
onClick={() => setShowGenerateModal(true)}
|
||||
startContent={<Sparkles className="w-4 h-4" />}
|
||||
>
|
||||
{dataSources
|
||||
.filter((ds) => !(agent.ragDataSources || []).includes(ds._id))
|
||||
.map((ds) => (
|
||||
<SelectItem key={ds._id}>
|
||||
{ds.name}
|
||||
</SelectItem>
|
||||
))
|
||||
}
|
||||
</Select>
|
||||
Generate
|
||||
</CustomButton>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
{(agent.ragDataSources || []).map((source) => {
|
||||
const ds = dataSources.find((ds) => ds._id === source);
|
||||
return (
|
||||
<div
|
||||
key={source}
|
||||
className="flex items-center justify-between p-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-750 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center justify-center w-8 h-8 rounded-md bg-indigo-50 dark:bg-indigo-900/20">
|
||||
<svg
|
||||
className="w-4 h-4 text-indigo-600 dark:text-indigo-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{ds?.name || "Unknown"}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Data Source
|
||||
</span>
|
||||
</div>
|
||||
{isInstructionsMaximized ? (
|
||||
<div className="fixed inset-0 z-50 bg-white dark:bg-gray-900">
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Instructions
|
||||
</label>
|
||||
<CustomButton
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => setIsInstructionsMaximized(false)}
|
||||
showHoverContent={true}
|
||||
hoverContent="Minimize"
|
||||
>
|
||||
<Minimize2 className="w-4 h-4" />
|
||||
</CustomButton>
|
||||
</div>
|
||||
<CustomButton
|
||||
variant="tertiary"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
className="text-gray-500 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20"
|
||||
onClick={() => {
|
||||
const newSources = agent.ragDataSources?.filter((s) => s !== source);
|
||||
handleUpdate({
|
||||
...agent,
|
||||
ragDataSources: newSources
|
||||
});
|
||||
}}
|
||||
startContent={<Trash2 className="w-4 h-4" />}
|
||||
onClick={() => setShowGenerateModal(true)}
|
||||
startContent={<Sparkles className="w-4 h-4" />}
|
||||
>
|
||||
Remove
|
||||
Generate
|
||||
</CustomButton>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<div className="flex-1 overflow-hidden p-4">
|
||||
<EditableField
|
||||
key="instructions-maximized"
|
||||
value={agent.instructions}
|
||||
onChange={(value) => {
|
||||
handleUpdate({
|
||||
...agent,
|
||||
instructions: value
|
||||
});
|
||||
}}
|
||||
markdown
|
||||
multiline
|
||||
mentions
|
||||
mentionsAtValues={atMentions}
|
||||
showSaveButton={true}
|
||||
showDiscardButton={true}
|
||||
className="h-full min-h-0 overflow-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<EditableField
|
||||
key="instructions"
|
||||
value={agent.instructions}
|
||||
onChange={(value) => {
|
||||
handleUpdate({
|
||||
...agent,
|
||||
instructions: value
|
||||
});
|
||||
}}
|
||||
markdown
|
||||
multiline
|
||||
mentions
|
||||
mentionsAtValues={atMentions}
|
||||
showSaveButton={true}
|
||||
showDiscardButton={true}
|
||||
className="h-full min-h-0 overflow-auto"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeTab === 'examples' && (
|
||||
<>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Examples
|
||||
</label>
|
||||
<CustomButton
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => setIsExamplesMaximized(!isExamplesMaximized)}
|
||||
showHoverContent={true}
|
||||
hoverContent={isExamplesMaximized ? "Minimize" : "Maximize"}
|
||||
>
|
||||
{isExamplesMaximized ? (
|
||||
<Minimize2 className="w-4 h-4" />
|
||||
) : (
|
||||
<Maximize2 className="w-4 h-4" />
|
||||
)}
|
||||
</CustomButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{agent.ragDataSources !== undefined && agent.ragDataSources.length > 0 && (
|
||||
<>
|
||||
<div className="mt-4">
|
||||
<button
|
||||
onClick={() => setIsAdvancedConfigOpen(!isAdvancedConfigOpen)}
|
||||
className="flex items-center gap-2 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase hover:text-gray-700 dark:hover:text-gray-300 transition-colors"
|
||||
>
|
||||
{isAdvancedConfigOpen ?
|
||||
<ChevronDown className="w-4 h-4 text-gray-400" /> :
|
||||
<ChevronRight className="w-4 h-4 text-gray-400" />
|
||||
}
|
||||
Advanced RAG configuration
|
||||
</button>
|
||||
|
||||
{isAdvancedConfigOpen && (
|
||||
<div className="mt-3 ml-4 p-4 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50">
|
||||
<div className="grid gap-6">
|
||||
<div className="space-y-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Return type
|
||||
</label>
|
||||
<div className="flex gap-4">
|
||||
{["chunks", "content"].map((type) => (
|
||||
<button
|
||||
key={type}
|
||||
onClick={() => handleUpdate({
|
||||
...agent,
|
||||
ragReturnType: type as z.infer<typeof WorkflowAgent>['ragReturnType']
|
||||
})}
|
||||
className={clsx(
|
||||
"px-4 py-2 rounded-lg text-sm font-medium transition-colors",
|
||||
agent.ragReturnType === type
|
||||
? "bg-indigo-50 dark:bg-indigo-900/20 text-indigo-600 dark:text-indigo-400 border-2 border-indigo-200 dark:border-indigo-800"
|
||||
: "bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-400 border border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600"
|
||||
)}
|
||||
>
|
||||
{type.charAt(0).toUpperCase() + type.slice(1)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Number of matches
|
||||
</label>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
max="20"
|
||||
className="w-24 px-3 py-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm focus:ring-2 focus:ring-indigo-500/20 dark:focus:ring-indigo-400/20 focus:border-indigo-500 dark:focus:border-indigo-400"
|
||||
value={agent.ragK}
|
||||
onChange={(e) => handleUpdate({
|
||||
...agent,
|
||||
ragK: parseInt(e.target.value)
|
||||
})}
|
||||
/>
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">
|
||||
matches
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Number of relevant chunks to retrieve (1-20)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{isExamplesMaximized ? (
|
||||
<div className="fixed inset-0 z-50 bg-white dark:bg-gray-900">
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Examples
|
||||
</label>
|
||||
<CustomButton
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => setIsExamplesMaximized(false)}
|
||||
showHoverContent={true}
|
||||
hoverContent="Minimize"
|
||||
>
|
||||
<Minimize2 className="w-4 h-4" />
|
||||
</CustomButton>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-hidden p-4">
|
||||
<EditableField
|
||||
key="examples-maximized"
|
||||
value={agent.examples || ""}
|
||||
onChange={(value) => {
|
||||
handleUpdate({
|
||||
...agent,
|
||||
examples: value
|
||||
});
|
||||
}}
|
||||
placeholder="Enter examples for this agent"
|
||||
markdown
|
||||
multiline
|
||||
mentions
|
||||
mentionsAtValues={atMentions}
|
||||
showSaveButton={true}
|
||||
showDiscardButton={true}
|
||||
className="h-full min-h-0 overflow-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<EditableField
|
||||
key="examples"
|
||||
value={agent.examples || ""}
|
||||
onChange={(value) => {
|
||||
handleUpdate({
|
||||
...agent,
|
||||
examples: value
|
||||
});
|
||||
}}
|
||||
placeholder="Enter examples for this agent"
|
||||
markdown
|
||||
multiline
|
||||
mentions
|
||||
mentionsAtValues={atMentions}
|
||||
showSaveButton={true}
|
||||
showDiscardButton={true}
|
||||
className="h-full min-h-0 overflow-auto"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeTab === 'configurations' && (
|
||||
<div className="space-y-6">
|
||||
{!agent.locked && (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Name
|
||||
</label>
|
||||
<div className={clsx(
|
||||
"border rounded-lg focus-within:ring-2",
|
||||
nameError
|
||||
? "border-red-500 focus-within:ring-red-500/20"
|
||||
: "border-gray-200 dark:border-gray-700 focus-within:ring-indigo-500/20 dark:focus-within:ring-indigo-400/20"
|
||||
)}>
|
||||
<Textarea
|
||||
value={agent.name}
|
||||
useValidation={true}
|
||||
updateOnBlur={true}
|
||||
validate={(value) => {
|
||||
const error = validateAgentName(value, agent.name, usedAgentNames);
|
||||
setNameError(error);
|
||||
return { valid: !error, errorMessage: error || undefined };
|
||||
}}
|
||||
onValidatedChange={(value) => {
|
||||
handleUpdate({
|
||||
...agent,
|
||||
name: value
|
||||
});
|
||||
}}
|
||||
placeholder="Enter agent name..."
|
||||
className="w-full text-sm bg-transparent focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 transition-colors px-4 py-3"
|
||||
autoResize
|
||||
/>
|
||||
</div>
|
||||
{nameError && (
|
||||
<p className="text-sm text-red-500">{nameError}</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Description
|
||||
</label>
|
||||
<Textarea
|
||||
value={agent.description || ""}
|
||||
onChange={(e) => {
|
||||
handleUpdate({
|
||||
...agent,
|
||||
description: e.target.value
|
||||
});
|
||||
}}
|
||||
placeholder="Enter a description for this agent"
|
||||
className={textareaStyles}
|
||||
autoResize
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Agent Type
|
||||
</label>
|
||||
<div className="relative ml-2 group">
|
||||
<Info
|
||||
className="w-4 h-4 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 cursor-pointer transition-colors"
|
||||
/>
|
||||
<div className="absolute bottom-full left-0 mb-2 p-3 w-80 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 text-xs invisible group-hover:visible z-50">
|
||||
<div className="mb-1 font-medium">Agent Types</div>
|
||||
Conversation agents' responses are user-facing. You can use conversation agents for multi-turn conversations with users.
|
||||
<br />
|
||||
<br />
|
||||
Task agents' responses are internal and available to other agents. You can use them to build pipelines and DAGs within workflows. E.g. Conversation Agent {'->'} Task Agent {'->'} Task Agent.
|
||||
<div className="absolute h-2 w-2 bg-white dark:bg-gray-800 transform rotate-45 -bottom-1 left-4 border-r border-b border-gray-200 dark:border-gray-700"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CustomDropdown
|
||||
value={agent.outputVisibility}
|
||||
options={[
|
||||
{ key: "user_facing", label: "Conversation Agent" },
|
||||
{ key: "internal", label: "Task Agent" }
|
||||
]}
|
||||
onChange={(value) => handleUpdate({
|
||||
...agent,
|
||||
outputVisibility: value as z.infer<typeof WorkflowAgent>['outputVisibility']
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{useRag && (
|
||||
<div className="space-y-4">
|
||||
<label className={sectionHeaderStyles}>
|
||||
RAG
|
||||
</label>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div>
|
||||
<Select
|
||||
variant="bordered"
|
||||
placeholder="Add data source"
|
||||
size="sm"
|
||||
className="w-64"
|
||||
onSelectionChange={(keys) => {
|
||||
const key = keys.currentKey as string;
|
||||
if (key) {
|
||||
handleUpdate({
|
||||
...agent,
|
||||
ragDataSources: [...(agent.ragDataSources || []), key]
|
||||
});
|
||||
}
|
||||
}}
|
||||
startContent={<PlusIcon className="w-4 h-4 text-gray-500" />}
|
||||
>
|
||||
{dataSources
|
||||
.filter((ds) => !(agent.ragDataSources || []).includes(ds._id))
|
||||
.map((ds) => (
|
||||
<SelectItem key={ds._id}>
|
||||
{ds.name}
|
||||
</SelectItem>
|
||||
))
|
||||
}
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
{(agent.ragDataSources || []).map((source) => {
|
||||
const ds = dataSources.find((ds) => ds._id === source);
|
||||
return (
|
||||
<div
|
||||
key={source}
|
||||
className="flex items-center justify-between p-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-750 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center justify-center w-8 h-8 rounded-md bg-indigo-50 dark:bg-indigo-900/20">
|
||||
<svg
|
||||
className="w-4 h-4 text-indigo-600 dark:text-indigo-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{ds?.name || "Unknown"}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Data Source
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<CustomButton
|
||||
variant="tertiary"
|
||||
size="sm"
|
||||
className="text-gray-500 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20"
|
||||
onClick={() => {
|
||||
const newSources = agent.ragDataSources?.filter((s) => s !== source);
|
||||
handleUpdate({
|
||||
...agent,
|
||||
ragDataSources: newSources
|
||||
});
|
||||
}}
|
||||
startContent={<Trash2 className="w-4 h-4" />}
|
||||
>
|
||||
Remove
|
||||
</CustomButton>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{agent.ragDataSources !== undefined && agent.ragDataSources.length > 0 && (
|
||||
<>
|
||||
<div className="mt-4">
|
||||
<button
|
||||
onClick={() => setIsAdvancedConfigOpen(!isAdvancedConfigOpen)}
|
||||
className="flex items-center gap-2 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase hover:text-gray-700 dark:hover:text-gray-300 transition-colors"
|
||||
>
|
||||
{isAdvancedConfigOpen ?
|
||||
<ChevronDown className="w-4 h-4 text-gray-400" /> :
|
||||
<ChevronRight className="w-4 h-4 text-gray-400" />
|
||||
}
|
||||
Advanced RAG configuration
|
||||
</button>
|
||||
|
||||
{isAdvancedConfigOpen && (
|
||||
<div className="mt-3 ml-4 p-4 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50">
|
||||
<div className="grid gap-6">
|
||||
<div className="space-y-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Return type
|
||||
</label>
|
||||
<div className="flex gap-4">
|
||||
{["chunks", "content"].map((type) => (
|
||||
<button
|
||||
key={type}
|
||||
onClick={() => handleUpdate({
|
||||
...agent,
|
||||
ragReturnType: type as z.infer<typeof WorkflowAgent>['ragReturnType']
|
||||
})}
|
||||
className={clsx(
|
||||
"px-4 py-2 rounded-lg text-sm font-medium transition-colors",
|
||||
agent.ragReturnType === type
|
||||
? "bg-indigo-50 dark:bg-indigo-900/20 text-indigo-600 dark:text-indigo-400 border-2 border-indigo-200 dark:border-indigo-800"
|
||||
: "bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-400 border border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600"
|
||||
)}
|
||||
>
|
||||
{type.charAt(0).toUpperCase() + type.slice(1)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Number of matches
|
||||
</label>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
max="20"
|
||||
className="w-24 px-3 py-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm focus:ring-2 focus:ring-indigo-500/20 dark:focus:ring-indigo-400/20 focus:border-indigo-500 dark:focus:border-indigo-400"
|
||||
value={agent.ragK}
|
||||
onChange={(e) => handleUpdate({
|
||||
...agent,
|
||||
ragK: parseInt(e.target.value)
|
||||
})}
|
||||
/>
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">
|
||||
matches
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Number of relevant chunks to retrieve (1-20)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Model
|
||||
</label>
|
||||
<div className="relative ml-2 group">
|
||||
<Info
|
||||
className="w-4 h-4 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 cursor-pointer transition-colors"
|
||||
/>
|
||||
<div className="absolute bottom-full left-0 mb-2 p-3 w-80 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 text-xs invisible group-hover:visible z-50">
|
||||
<div className="mb-1 font-medium">Model Configuration</div>
|
||||
Set this according to the PROVIDER_BASE_URL you have set in your .env file (such as your LiteLLM, gateway).
|
||||
<br />
|
||||
<br />
|
||||
E.g. LiteLLM's naming convention is like: 'claude-3-7-sonnet-latest', but you may have set alias model names or might be using a different provider like openrouter, openai etc.
|
||||
<br />
|
||||
<br />
|
||||
By default, the model is set to gpt-4.1, assuming your OpenAI API key is set in PROVIDER_API_KEY and PROVIDER_BASE_URL is not set.
|
||||
<div className="absolute h-2 w-2 bg-white dark:bg-gray-800 transform rotate-45 -bottom-1 left-4 border-r border-b border-gray-200 dark:border-gray-700"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Input
|
||||
value={agent.model}
|
||||
onChange={(e) => handleUpdate({
|
||||
...agent,
|
||||
model: e.target.value as z.infer<typeof WorkflowAgent>['model']
|
||||
})}
|
||||
className="w-full max-w-64"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Max calls from parent agent per turn
|
||||
</label>
|
||||
<div className="relative ml-2 group">
|
||||
<Info
|
||||
className="w-4 h-4 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 cursor-pointer transition-colors"
|
||||
/>
|
||||
<div className="absolute bottom-full left-0 mb-2 p-3 w-80 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 text-xs invisible group-hover:visible z-50">
|
||||
<div className="mb-1 font-medium">Max Calls Configuration</div>
|
||||
This setting limits how many times a parent agent can call this agent in a single turn, to prevent infinite loops.
|
||||
<div className="absolute h-2 w-2 bg-white dark:bg-gray-800 transform rotate-45 -bottom-1 left-4 border-r border-b border-gray-200 dark:border-gray-700"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Input
|
||||
type="number"
|
||||
min="1"
|
||||
value={agent.maxCallsPerParentAgent || 3}
|
||||
onChange={(e) => handleUpdate({
|
||||
...agent,
|
||||
maxCallsPerParentAgent: parseInt(e.target.value)
|
||||
})}
|
||||
className="w-full max-w-24"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{USE_TRANSFER_CONTROL_OPTIONS && (
|
||||
<div className="space-y-4">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Conversation control after turn
|
||||
</label>
|
||||
<CustomDropdown
|
||||
value={agent.controlType}
|
||||
options={[
|
||||
{ key: "retain", label: "Retain control" },
|
||||
{ key: "relinquish_to_parent", label: "Relinquish to parent" },
|
||||
{ key: "relinquish_to_start", label: "Relinquish to 'start' agent" }
|
||||
]}
|
||||
onChange={(value) => handleUpdate({
|
||||
...agent,
|
||||
controlType: value as z.infer<typeof WorkflowAgent>['controlType']
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Model
|
||||
</label>
|
||||
<div className="relative ml-2 group">
|
||||
<Info
|
||||
className="w-4 h-4 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 cursor-pointer transition-colors"
|
||||
/>
|
||||
<div className="absolute bottom-full left-0 mb-2 p-3 w-80 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 text-xs invisible group-hover:visible z-50">
|
||||
<div className="mb-1 font-medium">Model Configuration</div>
|
||||
Set this according to the PROVIDER_BASE_URL you have set in your .env file (such as your LiteLLM, gateway).
|
||||
<br />
|
||||
<br />
|
||||
E.g. LiteLLM's naming convention is like: 'claude-3-7-sonnet-latest', but you may have set alias model names or might be using a different provider like openrouter, openai etc.
|
||||
<br />
|
||||
<br />
|
||||
By default, the model is set to gpt-4.1, assuming your OpenAI API key is set in PROVIDER_API_KEY and PROVIDER_BASE_URL is not set.
|
||||
<div className="absolute h-2 w-2 bg-white dark:bg-gray-800 transform rotate-45 -bottom-1 left-4 border-r border-b border-gray-200 dark:border-gray-700"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Input
|
||||
value={agent.model}
|
||||
onChange={(e) => handleUpdate({
|
||||
...agent,
|
||||
model: e.target.value as z.infer<typeof WorkflowAgent>['model']
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{USE_TRANSFER_CONTROL_OPTIONS && (
|
||||
<div className="space-y-4">
|
||||
<label className={sectionHeaderStyles}>
|
||||
Conversation control after turn
|
||||
</label>
|
||||
<CustomDropdown
|
||||
value={agent.controlType}
|
||||
options={[
|
||||
{ key: "retain", label: "Retain control" },
|
||||
{ key: "relinquish_to_parent", label: "Relinquish to parent" },
|
||||
{ key: "relinquish_to_start", label: "Relinquish to 'start' agent" }
|
||||
]}
|
||||
onChange={(value) => handleUpdate({
|
||||
...agent,
|
||||
controlType: value as z.infer<typeof WorkflowAgent>['controlType']
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<PreviewModalProvider>
|
||||
<GenerateInstructionsModal
|
||||
projectId={projectId}
|
||||
|
|
|
|||
|
|
@ -61,11 +61,10 @@ export function PromptConfig({
|
|||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={handleClose}
|
||||
startContent={<XIcon className="w-4 h-4" />}
|
||||
aria-label="Close prompt config"
|
||||
className="transition-colors"
|
||||
showHoverContent={true}
|
||||
hoverContent="Close"
|
||||
>
|
||||
Close
|
||||
<XIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -256,10 +256,10 @@ export function ToolConfig({
|
|||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={handleClose}
|
||||
startContent={<XIcon className="w-4 h-4" />}
|
||||
aria-label="Close tool config"
|
||||
showHoverContent={true}
|
||||
hoverContent="Close"
|
||||
>
|
||||
Close
|
||||
<XIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { apiV1 } from "rowboat-shared";
|
|||
import { TestProfile } from "@/app/lib/types/testing_types";
|
||||
import { WithStringId } from "@/app/lib/types/types";
|
||||
import { ProfileSelector } from "@/app/projects/[projectId]/test/[[...slug]]/components/selectors/profile-selector";
|
||||
import { CheckIcon, CopyIcon, PlusIcon, UserIcon, InfoIcon } from "lucide-react";
|
||||
import { CheckIcon, CopyIcon, PlusIcon, UserIcon, InfoIcon, BugIcon, BugOffIcon } from "lucide-react";
|
||||
import { USE_TESTING_FEATURE } from "@/app/lib/feature_flags";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
|
|
@ -39,6 +39,7 @@ export function App({
|
|||
const [counter, setCounter] = useState<number>(0);
|
||||
const [testProfile, setTestProfile] = useState<WithStringId<z.infer<typeof TestProfile>> | null>(null);
|
||||
const [systemMessage, setSystemMessage] = useState<string>(defaultSystemMessage);
|
||||
const [showDebugMessages, setShowDebugMessages] = useState<boolean>(true);
|
||||
const [chat, setChat] = useState<z.infer<typeof PlaygroundChat>>({
|
||||
projectId,
|
||||
createdAt: new Date().toISOString(),
|
||||
|
|
@ -116,6 +117,20 @@ export function App({
|
|||
>
|
||||
<PlusIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={() => setShowDebugMessages(!showDebugMessages)}
|
||||
className={showDebugMessages ? "bg-blue-50 text-blue-700 hover:bg-blue-100" : "bg-gray-50 text-gray-500 hover:bg-gray-100"}
|
||||
showHoverContent={true}
|
||||
hoverContent={showDebugMessages ? "Hide debug messages" : "Show debug messages"}
|
||||
>
|
||||
{showDebugMessages ? (
|
||||
<BugIcon className="w-4 h-4" />
|
||||
) : (
|
||||
<BugOffIcon className="w-4 h-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
rightActions={
|
||||
|
|
@ -146,9 +161,6 @@ export function App({
|
|||
</Button>
|
||||
</div>
|
||||
}
|
||||
className={clsx(
|
||||
isInitialState && "opacity-50 transition-opacity duration-300"
|
||||
)}
|
||||
onClick={onPanelClick}
|
||||
>
|
||||
<ProfileSelector
|
||||
|
|
@ -172,6 +184,7 @@ export function App({
|
|||
mcpServerUrls={mcpServerUrls}
|
||||
toolWebhookUrl={toolWebhookUrl}
|
||||
onCopyClick={(fn) => { getCopyContentRef.current = fn; }}
|
||||
showDebugMessages={showDebugMessages}
|
||||
/>
|
||||
</div>
|
||||
</Panel>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export function Chat({
|
|||
mcpServerUrls,
|
||||
toolWebhookUrl,
|
||||
onCopyClick,
|
||||
showDebugMessages = true,
|
||||
}: {
|
||||
chat: z.infer<typeof PlaygroundChat>;
|
||||
projectId: string;
|
||||
|
|
@ -40,6 +41,7 @@ export function Chat({
|
|||
mcpServerUrls: Array<z.infer<typeof MCPServer>>;
|
||||
toolWebhookUrl: string;
|
||||
onCopyClick: (fn: () => string) => void;
|
||||
showDebugMessages?: boolean;
|
||||
}) {
|
||||
const [messages, setMessages] = useState<z.infer<typeof apiV1.ChatMessage>[]>(chat.messages);
|
||||
const [loadingAssistantResponse, setLoadingAssistantResponse] = useState<boolean>(false);
|
||||
|
|
@ -164,7 +166,7 @@ export function Chat({
|
|||
return;
|
||||
}
|
||||
|
||||
eventSource = new EventSource(`/api/v1/stream-response/${streamId}`);
|
||||
eventSource = new EventSource(`/api/stream-response/${streamId}`);
|
||||
|
||||
eventSource.addEventListener("message", (event) => {
|
||||
if (ignore) {
|
||||
|
|
@ -285,6 +287,7 @@ export function Chat({
|
|||
systemMessage={systemMessage}
|
||||
onSystemMessageChange={onSystemMessageChange}
|
||||
showSystemMessage={false}
|
||||
showDebugMessages={showDebugMessages}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,22 +6,21 @@ import { Workflow } from "@/app/lib/types/workflow_types";
|
|||
import { WorkflowTool } from "@/app/lib/types/workflow_types";
|
||||
import MarkdownContent from "@/app/lib/components/markdown-content";
|
||||
import { apiV1 } from "rowboat-shared";
|
||||
import { MessageSquareIcon, EllipsisIcon, CircleCheckIcon, ChevronRightIcon, ChevronDownIcon, XIcon } from "lucide-react";
|
||||
import { MessageSquareIcon, EllipsisIcon, CircleCheckIcon, ChevronRightIcon, ChevronDownIcon, ChevronUpIcon, XIcon, PlusIcon } from "lucide-react";
|
||||
import { TestProfile } from "@/app/lib/types/testing_types";
|
||||
import { ProfileContextBox } from "./profile-context-box";
|
||||
|
||||
function UserMessage({ content }: { content: string }) {
|
||||
return (
|
||||
<div className="self-end flex flex-col items-end gap-1">
|
||||
<div className="self-end flex flex-col items-end gap-1 mt-5 mb-8">
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs">
|
||||
User
|
||||
</div>
|
||||
<div className="max-w-[85%] inline-block">
|
||||
<div className="bg-blue-50 dark:bg-[#1e2023] px-4 py-2.5
|
||||
<div className="bg-blue-100 dark:bg-blue-900/40 px-4 py-2.5
|
||||
rounded-2xl rounded-br-lg text-sm leading-relaxed
|
||||
text-gray-700 dark:text-gray-200
|
||||
border border-blue-100 dark:border-[#2a2d31]
|
||||
shadow-sm animate-slideUpAndFade">
|
||||
text-gray-800 dark:text-blue-100
|
||||
border-none shadow-sm animate-slideUpAndFade">
|
||||
<div className="text-left">
|
||||
<MarkdownContent content={content} />
|
||||
</div>
|
||||
|
|
@ -31,52 +30,77 @@ function UserMessage({ content }: { content: string }) {
|
|||
);
|
||||
}
|
||||
|
||||
function InternalAssistantMessage({ content, sender, latency }: { content: string, sender: string | null | undefined, latency: number }) {
|
||||
function InternalAssistantMessage({ content, sender, latency, delta }: { content: string, sender: string | null | undefined, latency: number, delta: number }) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
// Show plus icon and duration
|
||||
const deltaDisplay = (
|
||||
<span className="inline-flex items-center text-gray-400 dark:text-gray-500">
|
||||
+{Math.round(delta / 1000)}s
|
||||
</span>
|
||||
);
|
||||
|
||||
// Get first line preview
|
||||
const firstLine = content.split('\n')[0].trim();
|
||||
const preview = firstLine.length > 50 ? firstLine.substring(0, 50) + '...' : firstLine;
|
||||
|
||||
return (
|
||||
<div className="self-start flex flex-col gap-1">
|
||||
{!expanded ? (
|
||||
<button className="flex items-center text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300 gap-1 group"
|
||||
onClick={() => setExpanded(true)}>
|
||||
<MessageSquareIcon size={16} />
|
||||
<EllipsisIcon size={16} />
|
||||
<span className="text-xs">Show debug message</span>
|
||||
</button>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs pl-1 flex items-center justify-between">
|
||||
<span>{sender ?? 'Assistant'}</span>
|
||||
<button className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
onClick={() => setExpanded(false)}>
|
||||
<XIcon size={16} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="max-w-[85%] inline-block">
|
||||
<div className="border border-gray-200 dark:border-gray-700 border-dashed
|
||||
px-4 py-2.5 rounded-2xl rounded-bl-lg text-sm
|
||||
text-gray-700 dark:text-gray-200 shadow-sm">
|
||||
<pre className="whitespace-pre-wrap">{content}</pre>
|
||||
<div className="self-start flex flex-col gap-1 my-5">
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs pl-1">
|
||||
{sender ?? 'Assistant'}
|
||||
</div>
|
||||
<div className={expanded ? 'max-w-[85%] inline-block' : 'inline-block'}>
|
||||
<div className={expanded
|
||||
? 'bg-gray-50 dark:bg-zinc-800 px-4 py-2.5 rounded-2xl rounded-bl-lg text-sm leading-relaxed text-gray-700 dark:text-gray-200 border-none shadow-sm animate-slideUpAndFade flex flex-col items-stretch'
|
||||
: 'bg-gray-50 dark:bg-zinc-800 px-4 py-2.5 rounded-2xl rounded-bl-lg text-sm leading-relaxed text-gray-700 dark:text-gray-200 border-none shadow-sm animate-slideUpAndFade w-fit'}>
|
||||
{!expanded ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-gray-700 dark:text-gray-200">
|
||||
{preview}
|
||||
</div>
|
||||
<div className="flex justify-between items-center gap-6">
|
||||
<button className="flex items-center gap-1 text-xs text-gray-600 dark:text-gray-300 hover:underline self-start" onClick={() => setExpanded(true)}>
|
||||
<ChevronDownIcon size={16} />
|
||||
Show internal message
|
||||
</button>
|
||||
<div className="text-right text-xs">
|
||||
{deltaDisplay}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
) : (
|
||||
<>
|
||||
<div className="text-left mb-2">
|
||||
<MarkdownContent content={content} />
|
||||
</div>
|
||||
<div className="flex justify-between items-center gap-6 mt-2">
|
||||
<button className="flex items-center gap-1 text-xs text-gray-600 dark:text-gray-300 hover:underline self-start" onClick={() => setExpanded(false)}>
|
||||
<ChevronUpIcon size={16} />
|
||||
Hide internal message
|
||||
</button>
|
||||
<div className="text-right text-xs">
|
||||
{deltaDisplay}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AssistantMessage({ content, sender, latency }: { content: string, sender: string | null | undefined, latency: number }) {
|
||||
return (
|
||||
<div className="self-start flex flex-col gap-1">
|
||||
<div className="self-start flex flex-col gap-1 my-5">
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs pl-1">
|
||||
{sender ?? 'Assistant'}
|
||||
</div>
|
||||
<div className="max-w-[85%] inline-block">
|
||||
<div className="bg-gray-50 dark:bg-[#1e2023] px-4 py-2.5
|
||||
<div className="bg-purple-50 dark:bg-purple-900/30 px-4 py-2.5
|
||||
rounded-2xl rounded-bl-lg text-sm leading-relaxed
|
||||
text-gray-700 dark:text-gray-200
|
||||
border border-gray-200 dark:border-[#2a2d31]
|
||||
shadow-sm animate-slideUpAndFade">
|
||||
text-gray-800 dark:text-purple-100
|
||||
border-none shadow-sm animate-slideUpAndFade">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="text-left">
|
||||
<MarkdownContent content={content} />
|
||||
|
|
@ -93,15 +117,11 @@ function AssistantMessage({ content, sender, latency }: { content: string, sende
|
|||
|
||||
function AssistantMessageLoading() {
|
||||
return (
|
||||
<div className="self-start flex flex-col gap-1">
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs pl-1">
|
||||
Assistant
|
||||
</div>
|
||||
<div className="self-start flex flex-col gap-1 my-5">
|
||||
<div className="max-w-[85%] inline-block">
|
||||
<div className="bg-gray-50 dark:bg-gray-800 px-4 py-2.5
|
||||
<div className="bg-purple-50 dark:bg-purple-900/30 px-4 py-2.5
|
||||
rounded-2xl rounded-bl-lg
|
||||
border border-gray-200 dark:border-gray-700
|
||||
shadow-sm dark:shadow-gray-950/20 animate-pulse min-h-[2.5rem] flex items-center">
|
||||
border-none shadow-sm animate-slideUpAndFade min-h-[2.5rem] flex items-center">
|
||||
<Spinner size="sm" className="ml-2" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -118,6 +138,7 @@ function ToolCalls({
|
|||
workflow,
|
||||
testProfile = null,
|
||||
systemMessage,
|
||||
delta
|
||||
}: {
|
||||
toolCalls: z.infer<typeof apiV1.AssistantMessageWithToolCalls>['tool_calls'];
|
||||
results: Record<string, z.infer<typeof apiV1.ToolMessage>>;
|
||||
|
|
@ -127,6 +148,7 @@ function ToolCalls({
|
|||
workflow: z.infer<typeof Workflow>;
|
||||
testProfile: z.infer<typeof TestProfile> | null;
|
||||
systemMessage: string | undefined;
|
||||
delta: number;
|
||||
}) {
|
||||
return <div className="flex flex-col gap-4">
|
||||
{toolCalls.map(toolCall => {
|
||||
|
|
@ -136,6 +158,7 @@ function ToolCalls({
|
|||
result={results[toolCall.id]}
|
||||
sender={sender}
|
||||
workflow={workflow}
|
||||
delta={delta}
|
||||
/>
|
||||
})}
|
||||
</div>;
|
||||
|
|
@ -146,11 +169,13 @@ function ToolCall({
|
|||
result,
|
||||
sender,
|
||||
workflow,
|
||||
delta
|
||||
}: {
|
||||
toolCall: z.infer<typeof apiV1.AssistantMessageWithToolCalls>['tool_calls'][number];
|
||||
result: z.infer<typeof apiV1.ToolMessage> | undefined;
|
||||
sender: string | null | undefined;
|
||||
workflow: z.infer<typeof Workflow>;
|
||||
delta: number;
|
||||
}) {
|
||||
let matchingWorkflowTool: z.infer<typeof WorkflowTool> | undefined;
|
||||
for (const tool of workflow.tools) {
|
||||
|
|
@ -163,45 +188,61 @@ function ToolCall({
|
|||
if (toolCall.function.name.startsWith('transfer_to_')) {
|
||||
return <TransferToAgentToolCall
|
||||
result={result}
|
||||
sender={sender}
|
||||
sender={sender ?? ''}
|
||||
delta={delta}
|
||||
/>;
|
||||
}
|
||||
return <ClientToolCall
|
||||
toolCall={toolCall}
|
||||
result={result}
|
||||
sender={sender}
|
||||
sender={sender ?? ''}
|
||||
workflow={workflow}
|
||||
delta={delta}
|
||||
/>;
|
||||
}
|
||||
|
||||
function TransferToAgentToolCall({
|
||||
result: availableResult,
|
||||
sender,
|
||||
delta
|
||||
}: {
|
||||
result: z.infer<typeof apiV1.ToolMessage> | undefined;
|
||||
sender: string | null | undefined;
|
||||
delta: number;
|
||||
}) {
|
||||
const typedResult = availableResult ? JSON.parse(availableResult.content) as { assistant: string } : undefined;
|
||||
if (!typedResult) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return <div className="flex gap-1 items-center text-gray-500 text-sm justify-center">
|
||||
<div>{sender}</div>
|
||||
<svg className="w-6 h-6" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
|
||||
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1" d="M19 12H5m14 0-4 4m4-4-4-4" />
|
||||
</svg>
|
||||
<div>{typedResult.assistant}</div>
|
||||
</div>;
|
||||
const deltaDisplay = (
|
||||
<span className="inline-flex items-center text-gray-400 dark:text-gray-500">
|
||||
+{Math.round(delta / 1000)}s
|
||||
</span>
|
||||
);
|
||||
return (
|
||||
<div className="flex justify-center mb-2">
|
||||
<div className="flex items-center gap-2 px-4 py-0.5 rounded-full bg-amber-50 dark:bg-amber-900/20 shadow-sm text-xs">
|
||||
<span className="text-gray-700 dark:text-gray-200">{sender}</span>
|
||||
<ChevronRightIcon size={14} className="text-gray-400 dark:text-gray-300" />
|
||||
<span className="text-gray-700 dark:text-gray-200">{typedResult.assistant}</span>
|
||||
<span className="ml-2">{deltaDisplay}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ClientToolCall({
|
||||
toolCall,
|
||||
result: availableResult,
|
||||
sender,
|
||||
workflow,
|
||||
delta
|
||||
}: {
|
||||
toolCall: z.infer<typeof apiV1.AssistantMessageWithToolCalls>['tool_calls'][number];
|
||||
result: z.infer<typeof apiV1.ToolMessage> | undefined;
|
||||
sender: string | null | undefined;
|
||||
workflow: z.infer<typeof Workflow>;
|
||||
delta: number;
|
||||
}) {
|
||||
return (
|
||||
<div className="self-start flex flex-col gap-1">
|
||||
|
|
@ -299,6 +340,7 @@ export function Messages({
|
|||
systemMessage,
|
||||
onSystemMessageChange,
|
||||
showSystemMessage,
|
||||
showDebugMessages = true,
|
||||
}: {
|
||||
projectId: string;
|
||||
messages: z.infer<typeof apiV1.ChatMessage>[];
|
||||
|
|
@ -309,6 +351,7 @@ export function Messages({
|
|||
systemMessage: string | undefined;
|
||||
onSystemMessageChange: (message: string) => void;
|
||||
showSystemMessage: boolean;
|
||||
showDebugMessages?: boolean;
|
||||
}) {
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
let lastUserMessageTimestamp = 0;
|
||||
|
|
@ -322,36 +365,53 @@ export function Messages({
|
|||
const isConsecutive = index > 0 && messages[index - 1].role === message.role;
|
||||
|
||||
if (message.role === 'assistant') {
|
||||
// the assistant message createdAt is an ISO string timestamp
|
||||
let latency = new Date(message.createdAt).getTime() - lastUserMessageTimestamp;
|
||||
// if this is the first message, set the latency to 0
|
||||
if (!userMessageSeen) {
|
||||
latency = 0;
|
||||
}
|
||||
if ('tool_calls' in message) {
|
||||
|
||||
// First check for tool calls
|
||||
if ('tool_calls' in message && message.tool_calls) {
|
||||
// Skip tool calls if debug mode is off
|
||||
if (!showDebugMessages) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<ToolCalls
|
||||
toolCalls={message.tool_calls}
|
||||
results={toolCallResults}
|
||||
projectId={projectId}
|
||||
messages={messages}
|
||||
sender={message.agenticSender}
|
||||
sender={message.agenticSender ?? ''}
|
||||
workflow={workflow}
|
||||
testProfile={testProfile}
|
||||
systemMessage={systemMessage}
|
||||
delta={latency}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return message.agenticResponseType === 'internal' ? (
|
||||
<InternalAssistantMessage
|
||||
content={message.content}
|
||||
sender={message.agenticSender}
|
||||
latency={latency}
|
||||
/>
|
||||
) : (
|
||||
|
||||
// Then check for internal messages
|
||||
if (message.agenticResponseType === 'internal') {
|
||||
// Skip internal messages if debug mode is off
|
||||
if (!showDebugMessages) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<InternalAssistantMessage
|
||||
content={message.content ?? ''}
|
||||
sender={message.agenticSender ?? ''}
|
||||
latency={latency}
|
||||
delta={latency}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Finally, regular assistant messages
|
||||
return (
|
||||
<AssistantMessage
|
||||
content={message.content}
|
||||
sender={message.agenticSender}
|
||||
content={message.content ?? ''}
|
||||
sender={message.agenticSender ?? ''}
|
||||
latency={latency}
|
||||
/>
|
||||
);
|
||||
|
|
@ -366,6 +426,14 @@ export function Messages({
|
|||
return null;
|
||||
};
|
||||
|
||||
const isAgentTransition = (message: z.infer<typeof apiV1.ChatMessage>) => {
|
||||
return message.role === 'assistant' && 'tool_calls' in message && Array.isArray(message.tool_calls) && message.tool_calls.some(tc => tc.function.name.startsWith('transfer_to_'));
|
||||
};
|
||||
|
||||
const isAssistantMessage = (message: z.infer<typeof apiV1.ChatMessage>) => {
|
||||
return message.role === 'assistant' && (!('tool_calls' in message) || !Array.isArray(message.tool_calls) || !message.tool_calls.some(tc => tc.function.name.startsWith('transfer_to_')));
|
||||
};
|
||||
|
||||
if (showSystemMessage) {
|
||||
return (
|
||||
<ProfileContextBox
|
||||
|
|
@ -378,15 +446,18 @@ export function Messages({
|
|||
|
||||
return (
|
||||
<div className="max-w-[768px] mx-auto">
|
||||
<div className="flex flex-col space-y-2">
|
||||
{messages.map((message, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`${index > 0 && messages[index - 1].role === message.role ? 'mt-1' : 'mt-4'}`}
|
||||
>
|
||||
{renderMessage(message, index)}
|
||||
</div>
|
||||
))}
|
||||
<div className="flex flex-col">
|
||||
{messages.map((message, index) => {
|
||||
const renderedMessage = renderMessage(message, index);
|
||||
if (renderedMessage) {
|
||||
return (
|
||||
<div key={index}>
|
||||
{renderedMessage}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
{loadingAssistantResponse && <AssistantMessageLoading />}
|
||||
</div>
|
||||
<div ref={messagesEndRef} />
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { AgentConfig } from "../entities/agent_config";
|
|||
import { ToolConfig } from "../entities/tool_config";
|
||||
import { App as ChatApp } from "../playground/app";
|
||||
import { z } from "zod";
|
||||
import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownSection, DropdownTrigger, Spinner } from "@heroui/react";
|
||||
import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, Spinner, Tooltip } from "@heroui/react";
|
||||
import { PromptConfig } from "../entities/prompt_config";
|
||||
import { EditableField } from "../../../lib/components/editable-field";
|
||||
import { RelativeTime } from "@primer/react";
|
||||
|
|
@ -27,7 +27,7 @@ import { apiV1 } from "rowboat-shared";
|
|||
import { publishWorkflow, renameWorkflow, saveWorkflow } from "../../../actions/workflow_actions";
|
||||
import { PublishedBadge } from "./published_badge";
|
||||
import { BackIcon, HamburgerIcon, WorkflowIcon } from "../../../lib/components/icons";
|
||||
import { CopyIcon, ImportIcon, Layers2Icon, RadioIcon, RedoIcon, ServerIcon, Sparkles, UndoIcon } from "lucide-react";
|
||||
import { CopyIcon, ImportIcon, Layers2Icon, RadioIcon, RedoIcon, ServerIcon, Sparkles, UndoIcon, RocketIcon, PenLine, AlertTriangle } from "lucide-react";
|
||||
import { EntityList } from "./entity_list";
|
||||
import { McpImportTools } from "./mcp_imports";
|
||||
import { ProductTour } from "@/components/common/product-tour";
|
||||
|
|
@ -269,6 +269,8 @@ function reducer(state: State, action: Action): State {
|
|||
ragReturnType: "chunks",
|
||||
ragK: 3,
|
||||
controlType: "retain",
|
||||
outputVisibility: "user_facing",
|
||||
maxCallsPerParentAgent: 3,
|
||||
...action.agent
|
||||
});
|
||||
draft.selection = {
|
||||
|
|
@ -784,28 +786,45 @@ export function WorkflowEditor({
|
|||
|
||||
return <div className="flex flex-col h-full relative">
|
||||
<div className="shrink-0 flex justify-between items-center pb-6">
|
||||
<div className="workflow-version-selector flex items-center gap-1 px-2 text-gray-800 dark:text-gray-100">
|
||||
<div className="workflow-version-selector flex items-center gap-4 px-2 text-gray-800 dark:text-gray-100">
|
||||
<WorkflowIcon size={16} />
|
||||
<EditableField
|
||||
key={state.present.workflow._id}
|
||||
value={state.present.workflow?.name || ''}
|
||||
onChange={handleRenameWorkflow}
|
||||
placeholder="Name this version"
|
||||
className="text-sm font-semibold"
|
||||
inline={true}
|
||||
/>
|
||||
{state.present.publishing && <Spinner size="sm" />}
|
||||
{isLive && <PublishedBadge />}
|
||||
<Tooltip content="Click to edit">
|
||||
<div>
|
||||
<EditableField
|
||||
key={state.present.workflow._id}
|
||||
value={state.present.workflow?.name || ''}
|
||||
onChange={handleRenameWorkflow}
|
||||
placeholder="Name this version"
|
||||
className="text-sm font-semibold"
|
||||
inline={true}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div className="flex items-center gap-2">
|
||||
{state.present.publishing && <Spinner size="sm" />}
|
||||
{isLive && <div className="bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400 px-3 py-1.5 rounded-md text-sm font-medium flex items-center gap-2">
|
||||
<RadioIcon size={16} />
|
||||
Live
|
||||
</div>}
|
||||
{!isLive && <div className="bg-yellow-50 dark:bg-yellow-900/20 text-yellow-600 dark:text-yellow-400 px-3 py-1.5 rounded-md text-sm font-medium flex items-center gap-2">
|
||||
<PenLine size={16} />
|
||||
Draft
|
||||
</div>}
|
||||
</div>
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
<button className="p-1 text-gray-400 hover:text-black">
|
||||
<HamburgerIcon size={16} />
|
||||
</button>
|
||||
<div>
|
||||
<Tooltip content="Version Menu">
|
||||
<button className="p-1.5 text-gray-500 hover:text-gray-800 transition-colors">
|
||||
<HamburgerIcon size={20} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu
|
||||
disabledKeys={[
|
||||
...(state.present.pendingChanges ? ['switch', 'clone'] : []),
|
||||
...(isLive ? ['publish', 'mcp'] : []),
|
||||
...(isLive ? ['mcp'] : []),
|
||||
]}
|
||||
onAction={(key) => {
|
||||
if (key === 'switch') {
|
||||
|
|
@ -814,51 +833,34 @@ export function WorkflowEditor({
|
|||
if (key === 'clone') {
|
||||
handleCloneVersion(state.present.workflow._id);
|
||||
}
|
||||
if (key === 'publish') {
|
||||
handlePublishWorkflow();
|
||||
}
|
||||
if (key === 'clipboard') {
|
||||
handleCopyJSON();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DropdownSection>
|
||||
<DropdownItem
|
||||
key="switch"
|
||||
startContent={<div className="text-gray-500"><BackIcon size={16} /></div>}
|
||||
className="gap-x-2"
|
||||
>
|
||||
View versions
|
||||
</DropdownItem>
|
||||
</DropdownSection>
|
||||
<DropdownItem
|
||||
key="switch"
|
||||
startContent={<div className="text-gray-500"><BackIcon size={16} /></div>}
|
||||
className="gap-x-2"
|
||||
>
|
||||
View versions
|
||||
</DropdownItem>
|
||||
|
||||
<DropdownSection>
|
||||
<DropdownItem
|
||||
key="clone"
|
||||
startContent={<div className="text-gray-500"><Layers2Icon size={16} /></div>}
|
||||
className="gap-x-2"
|
||||
>
|
||||
Clone this version
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
key="clone"
|
||||
startContent={<div className="text-gray-500"><Layers2Icon size={16} /></div>}
|
||||
className="gap-x-2"
|
||||
>
|
||||
Clone this version
|
||||
</DropdownItem>
|
||||
|
||||
<DropdownItem
|
||||
key="publish"
|
||||
startContent={<div className="text-indigo-500"><RadioIcon size={16} /></div>}
|
||||
className="gap-x-2 text-indigo-600 hover:bg-indigo-50 dark:hover:bg-indigo-900/20"
|
||||
>
|
||||
Make version live
|
||||
</DropdownItem>
|
||||
</DropdownSection>
|
||||
|
||||
<DropdownSection>
|
||||
<DropdownItem
|
||||
key="clipboard"
|
||||
startContent={<div className="text-gray-500"><CopyIcon size={16} /></div>}
|
||||
className="gap-x-2"
|
||||
>
|
||||
Export as JSON
|
||||
</DropdownItem>
|
||||
</DropdownSection>
|
||||
<DropdownItem
|
||||
key="clipboard"
|
||||
startContent={<div className="text-gray-500"><CopyIcon size={16} /></div>}
|
||||
className="gap-x-2"
|
||||
>
|
||||
Export as JSON
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
|
@ -867,16 +869,28 @@ export function WorkflowEditor({
|
|||
</div>}
|
||||
<div className="flex items-center gap-2">
|
||||
{isLive && <div className="flex items-center gap-2">
|
||||
<div className="bg-yellow-50 text-yellow-500 px-2 py-1 rounded-md text-sm">
|
||||
This version is locked. You cannot make changes.
|
||||
<div className="bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-400 px-3 py-1.5 rounded-md text-sm font-medium flex items-center gap-2">
|
||||
<AlertTriangle size={16} />
|
||||
This version is locked. You cannot make changes. Changes applied through copilot will<b>not</b>be reflected.
|
||||
</div>
|
||||
<Button
|
||||
variant="bordered"
|
||||
size="sm"
|
||||
variant="solid"
|
||||
size="md"
|
||||
onPress={() => handleCloneVersion(state.present.workflow._id)}
|
||||
className="gap-2 px-4 bg-amber-600 hover:bg-amber-700 text-white font-semibold text-sm"
|
||||
startContent={<Layers2Icon size={16} />}
|
||||
>
|
||||
Clone this version
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
size="md"
|
||||
onPress={() => setShowCopilot(!showCopilot)}
|
||||
className="gap-2 px-4 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold text-sm"
|
||||
startContent={showCopilot ? null : <Sparkles size={16} />}
|
||||
>
|
||||
{showCopilot ? "Hide Copilot" : "Copilot"}
|
||||
</Button>
|
||||
</div>}
|
||||
{!isLive && <div className="text-xs text-gray-400">
|
||||
{state.present.saving && <div className="flex items-center gap-1">
|
||||
|
|
@ -906,12 +920,22 @@ export function WorkflowEditor({
|
|||
</button>
|
||||
<Button
|
||||
variant="solid"
|
||||
size="lg"
|
||||
onPress={() => setShowCopilot(!showCopilot)}
|
||||
className="gap-2 px-6 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold text-base"
|
||||
startContent={<Sparkles size={20} />}
|
||||
size="md"
|
||||
onPress={handlePublishWorkflow}
|
||||
className="gap-2 px-4 bg-green-600 hover:bg-green-700 text-white font-semibold text-sm"
|
||||
startContent={<RocketIcon size={16} />}
|
||||
data-tour-target="deploy"
|
||||
>
|
||||
Copilot
|
||||
Deploy
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
size="md"
|
||||
onPress={() => setShowCopilot(!showCopilot)}
|
||||
className="gap-2 px-4 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold text-sm"
|
||||
startContent={showCopilot ? null : <Sparkles size={16} />}
|
||||
>
|
||||
{showCopilot ? "Hide Copilot" : "Copilot"}
|
||||
</Button>
|
||||
</>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {
|
|||
import { getProjectConfig } from "@/app/actions/project_actions";
|
||||
import { useTheme } from "@/app/providers/theme-provider";
|
||||
import { USE_TESTING_FEATURE, USE_PRODUCT_TOUR } from '@/app/lib/feature_flags';
|
||||
import { useHelpModal } from "@/app/providers/help-modal-provider";
|
||||
|
||||
interface SidebarProps {
|
||||
projectId: string;
|
||||
|
|
@ -36,6 +37,7 @@ export default function Sidebar({ projectId, useRag, useAuth, collapsed = false,
|
|||
const [projectName, setProjectName] = useState<string>("Select Project");
|
||||
const isProjectsRoute = pathname === '/projects' || pathname === '/projects/select';
|
||||
const { theme, toggleTheme } = useTheme();
|
||||
const { showHelpModal } = useHelpModal();
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchProjectName() {
|
||||
|
|
@ -79,116 +81,137 @@ export default function Sidebar({ projectId, useRag, useAuth, collapsed = false,
|
|||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<aside className={`${collapsed ? 'w-16' : 'w-60'} bg-transparent flex flex-col h-full transition-all duration-300`}>
|
||||
<div className="flex flex-col flex-grow">
|
||||
{!isProjectsRoute && (
|
||||
<>
|
||||
{/* Project Selector */}
|
||||
<div className="p-3 border-b border-zinc-100 dark:border-zinc-800">
|
||||
<Tooltip content={collapsed ? projectName : "Change project"} showArrow placement="right">
|
||||
<Link
|
||||
href="/projects"
|
||||
className={`
|
||||
flex items-center rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-800/50 transition-all
|
||||
${collapsed ? 'justify-center py-4' : 'gap-3 px-4 py-2.5'}
|
||||
`}
|
||||
>
|
||||
<FolderOpenIcon
|
||||
size={collapsed ? COLLAPSED_ICON_SIZE : EXPANDED_ICON_SIZE}
|
||||
className="text-zinc-500 dark:text-zinc-400 transition-all duration-200"
|
||||
/>
|
||||
{!collapsed && (
|
||||
<span className="text-sm font-medium truncate">
|
||||
{projectName}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</div>
|
||||
const handleStartTour = () => {
|
||||
localStorage.removeItem('user_product_tour_completed');
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
{/* Navigation Items */}
|
||||
<nav className="p-3 space-y-4">
|
||||
{navItems.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const fullPath = `/projects/${projectId}/${item.href}`;
|
||||
const isActive = pathname.startsWith(fullPath);
|
||||
const isDisabled = isProjectsRoute && item.requiresProject;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
key={item.href}
|
||||
content={collapsed ? item.label : ""}
|
||||
showArrow
|
||||
placement="right"
|
||||
return (
|
||||
<>
|
||||
<aside className={`${collapsed ? 'w-16' : 'w-60'} bg-transparent flex flex-col h-full transition-all duration-300`}>
|
||||
<div className="flex flex-col flex-grow">
|
||||
{!isProjectsRoute && (
|
||||
<>
|
||||
{/* Project Selector */}
|
||||
<div className="p-3 border-b border-zinc-100 dark:border-zinc-800">
|
||||
<Tooltip content={collapsed ? projectName : "Change project"} showArrow placement="right">
|
||||
<Link
|
||||
href="/projects"
|
||||
className={`
|
||||
flex items-center rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-800/50 transition-all
|
||||
${collapsed ? 'justify-center py-4' : 'gap-3 px-4 py-2.5'}
|
||||
`}
|
||||
>
|
||||
<Link
|
||||
href={isDisabled ? '#' : fullPath}
|
||||
className={isDisabled ? 'pointer-events-none' : ''}
|
||||
<FolderOpenIcon
|
||||
size={collapsed ? COLLAPSED_ICON_SIZE : EXPANDED_ICON_SIZE}
|
||||
className="text-zinc-500 dark:text-zinc-400 transition-all duration-200"
|
||||
/>
|
||||
{!collapsed && (
|
||||
<span className="text-sm font-medium truncate">
|
||||
{projectName}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
{/* Navigation Items */}
|
||||
<nav className="p-3 space-y-4">
|
||||
{navItems.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const fullPath = `/projects/${projectId}/${item.href}`;
|
||||
const isActive = pathname.startsWith(fullPath);
|
||||
const isDisabled = isProjectsRoute && item.requiresProject;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
key={item.href}
|
||||
content={collapsed ? item.label : ""}
|
||||
showArrow
|
||||
placement="right"
|
||||
>
|
||||
<button
|
||||
className={`
|
||||
relative w-full rounded-md flex items-center
|
||||
text-[15px] font-medium transition-all duration-200
|
||||
${collapsed ? 'justify-center py-4' : 'px-4 py-4 gap-3'}
|
||||
${isActive
|
||||
? 'bg-indigo-50 dark:bg-indigo-500/10 text-indigo-600 dark:text-indigo-400 border-l-2 border-indigo-600 dark:border-indigo-400'
|
||||
: isDisabled
|
||||
? 'text-zinc-300 dark:text-zinc-600 cursor-not-allowed'
|
||||
: 'text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800/50 hover:text-zinc-900 dark:hover:text-zinc-300'
|
||||
}
|
||||
`}
|
||||
disabled={isDisabled}
|
||||
data-tour-target={item.href === 'config' ? 'settings' : undefined}
|
||||
<Link
|
||||
href={isDisabled ? '#' : fullPath}
|
||||
className={isDisabled ? 'pointer-events-none' : ''}
|
||||
>
|
||||
<Icon
|
||||
size={collapsed ? COLLAPSED_ICON_SIZE : EXPANDED_ICON_SIZE}
|
||||
<button
|
||||
className={`
|
||||
transition-all duration-200
|
||||
${isDisabled
|
||||
? 'text-zinc-300 dark:text-zinc-600'
|
||||
: isActive
|
||||
? 'text-indigo-600 dark:text-indigo-400'
|
||||
: 'text-zinc-500 dark:text-zinc-400'
|
||||
relative w-full rounded-md flex items-center
|
||||
text-[15px] font-medium transition-all duration-200
|
||||
${collapsed ? 'justify-center py-4' : 'px-4 py-4 gap-3'}
|
||||
${isActive
|
||||
? 'bg-indigo-50 dark:bg-indigo-500/10 text-indigo-600 dark:text-indigo-400 border-l-2 border-indigo-600 dark:border-indigo-400'
|
||||
: isDisabled
|
||||
? 'text-zinc-300 dark:text-zinc-600 cursor-not-allowed'
|
||||
: 'text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800/50 hover:text-zinc-900 dark:hover:text-zinc-300'
|
||||
}
|
||||
`}
|
||||
/>
|
||||
{!collapsed && <span>{item.label}</span>}
|
||||
</button>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Bottom section */}
|
||||
<div className="mt-auto">
|
||||
{/* Collapse Toggle Button */}
|
||||
<div className="p-3 border-t border-zinc-100 dark:border-zinc-800">
|
||||
<button
|
||||
onClick={onToggleCollapse}
|
||||
className="w-full flex items-center justify-center p-2 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-800/50 transition-all"
|
||||
>
|
||||
{collapsed ? (
|
||||
<ChevronRightIcon size={20} className="text-zinc-500 dark:text-zinc-400" />
|
||||
) : (
|
||||
<ChevronLeftIcon size={20} className="text-zinc-500 dark:text-zinc-400" />
|
||||
)}
|
||||
</button>
|
||||
disabled={isDisabled}
|
||||
data-tour-target={item.href === 'config' ? 'settings' : undefined}
|
||||
>
|
||||
<Icon
|
||||
size={collapsed ? COLLAPSED_ICON_SIZE : EXPANDED_ICON_SIZE}
|
||||
className={`
|
||||
transition-all duration-200
|
||||
${isDisabled
|
||||
? 'text-zinc-300 dark:text-zinc-600'
|
||||
: isActive
|
||||
? 'text-indigo-600 dark:text-indigo-400'
|
||||
: 'text-zinc-500 dark:text-zinc-400'
|
||||
}
|
||||
`}
|
||||
/>
|
||||
{!collapsed && <span>{item.label}</span>}
|
||||
</button>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Theme and Auth Controls */}
|
||||
<div className="p-3 border-t border-zinc-100 dark:border-zinc-800 space-y-2">
|
||||
{USE_PRODUCT_TOUR && !isProjectsRoute && (
|
||||
<Tooltip content={collapsed ? "Take Tour" : ""} showArrow placement="right">
|
||||
{/* Bottom section */}
|
||||
<div className="mt-auto">
|
||||
{/* Collapse Toggle Button */}
|
||||
<div className="p-3 border-t border-zinc-100 dark:border-zinc-800">
|
||||
<button
|
||||
onClick={onToggleCollapse}
|
||||
className="w-full flex items-center justify-center p-2 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-800/50 transition-all"
|
||||
>
|
||||
{collapsed ? (
|
||||
<ChevronRightIcon size={20} className="text-zinc-500 dark:text-zinc-400" />
|
||||
) : (
|
||||
<ChevronLeftIcon size={20} className="text-zinc-500 dark:text-zinc-400" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Theme and Auth Controls */}
|
||||
<div className="p-3 border-t border-zinc-100 dark:border-zinc-800 space-y-2">
|
||||
{USE_PRODUCT_TOUR && !isProjectsRoute && (
|
||||
<Tooltip content={collapsed ? "Help" : ""} showArrow placement="right">
|
||||
<button
|
||||
onClick={showHelpModal}
|
||||
className={`
|
||||
w-full rounded-md flex items-center
|
||||
text-[15px] font-medium transition-all duration-200
|
||||
${collapsed ? 'justify-center py-4' : 'px-4 py-4 gap-3'}
|
||||
hover:bg-zinc-100 dark:hover:bg-zinc-800/50
|
||||
text-zinc-600 dark:text-zinc-400
|
||||
`}
|
||||
data-tour-target="tour-button"
|
||||
>
|
||||
<HelpCircle size={COLLAPSED_ICON_SIZE} />
|
||||
{!collapsed && <span>Help</span>}
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Tooltip content={collapsed ? "Appearance" : ""} showArrow placement="right">
|
||||
<button
|
||||
onClick={() => {
|
||||
localStorage.removeItem('user_product_tour_completed');
|
||||
window.location.reload();
|
||||
}}
|
||||
onClick={toggleTheme}
|
||||
className={`
|
||||
w-full rounded-md flex items-center
|
||||
text-[15px] font-medium transition-all duration-200
|
||||
|
|
@ -197,45 +220,29 @@ export default function Sidebar({ projectId, useRag, useAuth, collapsed = false,
|
|||
text-zinc-600 dark:text-zinc-400
|
||||
`}
|
||||
>
|
||||
<HelpCircle size={COLLAPSED_ICON_SIZE} />
|
||||
{!collapsed && <span>Take Tour</span>}
|
||||
{ theme == "light" ? <Moon size={COLLAPSED_ICON_SIZE} /> : <Sun size={COLLAPSED_ICON_SIZE} /> }
|
||||
{!collapsed && <span>Appearance</span>}
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Tooltip content={collapsed ? "Appearance" : ""} showArrow placement="right">
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
className={`
|
||||
w-full rounded-md flex items-center
|
||||
text-[15px] font-medium transition-all duration-200
|
||||
${collapsed ? 'justify-center py-4' : 'px-4 py-4 gap-3'}
|
||||
hover:bg-zinc-100 dark:hover:bg-zinc-800/50
|
||||
text-zinc-600 dark:text-zinc-400
|
||||
`}
|
||||
>
|
||||
{ theme == "light" ? <Moon size={COLLAPSED_ICON_SIZE} /> : <Sun size={COLLAPSED_ICON_SIZE} /> }
|
||||
{!collapsed && <span>Appearance</span>}
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
{useAuth && (
|
||||
<Tooltip content={collapsed ? "Account" : ""} showArrow placement="right">
|
||||
<div
|
||||
className={`
|
||||
w-full rounded-md flex items-center
|
||||
text-[15px] font-medium transition-all duration-200
|
||||
${collapsed ? 'justify-center py-4' : 'px-4 py-4 gap-3'}
|
||||
hover:bg-zinc-100 dark:hover:bg-zinc-800/50
|
||||
`}
|
||||
>
|
||||
<UserButton />
|
||||
{!collapsed && <span>Account</span>}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
{useAuth && (
|
||||
<Tooltip content={collapsed ? "Account" : ""} showArrow placement="right">
|
||||
<div
|
||||
className={`
|
||||
w-full rounded-md flex items-center
|
||||
text-[15px] font-medium transition-all duration-200
|
||||
${collapsed ? 'justify-center py-4' : 'px-4 py-4 gap-3'}
|
||||
hover:bg-zinc-100 dark:hover:bg-zinc-800/50
|
||||
`}
|
||||
>
|
||||
<UserButton />
|
||||
{!collapsed && <span>Account</span>}
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</aside>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -9,9 +9,10 @@ import { SectionHeading } from "@/components/ui/section-heading";
|
|||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Submit } from "./submit-button";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { FolderOpenIcon } from "@heroicons/react/24/outline";
|
||||
import { FolderOpenIcon, InformationCircleIcon } from "@heroicons/react/24/outline";
|
||||
import { USE_MULTIPLE_PROJECTS } from "@/app/lib/feature_flags";
|
||||
import { HorizontalDivider } from "@/components/ui/horizontal-divider";
|
||||
import { Tooltip } from "@heroui/react";
|
||||
|
||||
// Add glow animation styles
|
||||
const glowStyles = `
|
||||
|
|
@ -326,7 +327,7 @@ export function CreateProject({ defaultName, onOpenProjectPane, isProjectPaneOpe
|
|||
</svg>
|
||||
}
|
||||
>
|
||||
Customize an existing example
|
||||
Use an example
|
||||
</Button>
|
||||
|
||||
{isExamplesDropdownOpen && (
|
||||
|
|
@ -365,6 +366,14 @@ export function CreateProject({ defaultName, onOpenProjectPane, isProjectPaneOpe
|
|||
<label className={largeSectionHeaderStyles}>
|
||||
{selectedTab === TabType.Describe ? '✏️ What do you want to build?' : '✏️ Customize the description'}
|
||||
</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">
|
||||
In the next step, our AI copilot will create agents for you, complete with mock-tools.
|
||||
</p>
|
||||
<Tooltip content={<div>If you already know the specific agents and tools you need, mention them below.<br /><br />Specify 'internal agents' for task agents that will not interact with the user and 'user-facing agents' for conversational agents that will interact with users.</div>} className="max-w-[560px]">
|
||||
<InformationCircleIcon className="w-4 h-4 text-indigo-500 hover:text-indigo-600 dark:text-indigo-400 dark:hover:text-indigo-300 cursor-help" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Textarea
|
||||
value={customPrompt}
|
||||
|
|
|
|||
42
apps/rowboat/app/providers/help-modal-provider.tsx
Normal file
42
apps/rowboat/app/providers/help-modal-provider.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
'use client';
|
||||
|
||||
import { createContext, useContext, useState, ReactNode } from 'react';
|
||||
import { HelpModal } from '@/components/common/help-modal';
|
||||
|
||||
interface HelpModalContextType {
|
||||
showHelpModal: () => void;
|
||||
hideHelpModal: () => void;
|
||||
}
|
||||
|
||||
const HelpModalContext = createContext<HelpModalContextType | undefined>(undefined);
|
||||
|
||||
export function HelpModalProvider({ children }: { children: ReactNode }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const showHelpModal = () => setIsOpen(true);
|
||||
const hideHelpModal = () => setIsOpen(false);
|
||||
|
||||
const handleStartTour = () => {
|
||||
localStorage.removeItem('user_product_tour_completed');
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
return (
|
||||
<HelpModalContext.Provider value={{ showHelpModal, hideHelpModal }}>
|
||||
{children}
|
||||
<HelpModal
|
||||
isOpen={isOpen}
|
||||
onClose={hideHelpModal}
|
||||
onStartTour={handleStartTour}
|
||||
/>
|
||||
</HelpModalContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useHelpModal() {
|
||||
const context = useContext(HelpModalContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useHelpModal must be used within a HelpModalProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
92
apps/rowboat/components/common/help-modal.tsx
Normal file
92
apps/rowboat/components/common/help-modal.tsx
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import { Button } from "@heroui/react";
|
||||
import { HelpCircle, BookOpen, MessageCircle } from "lucide-react";
|
||||
|
||||
interface HelpModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onStartTour: () => void;
|
||||
}
|
||||
|
||||
export function HelpModal({ isOpen, onClose, onStartTour }: HelpModalProps) {
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-[100] flex items-center justify-center">
|
||||
<div className="bg-white dark:bg-zinc-800 rounded-lg shadow-lg p-6 w-[480px] max-w-[90vw] animate-in fade-in duration-200">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-6">
|
||||
Need Help?
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<Button
|
||||
className="w-full justify-start gap-4 text-left py-6 px-4 hover:bg-indigo-50 dark:hover:bg-indigo-500/10 transition-all duration-200 group hover:scale-[1.02] hover:shadow-md"
|
||||
variant="light"
|
||||
onPress={onStartTour}
|
||||
>
|
||||
<div className="bg-indigo-100 dark:bg-indigo-500/20 p-2 rounded-lg group-hover:bg-indigo-200 dark:group-hover:bg-indigo-500/30 transition-colors">
|
||||
<HelpCircle className="w-6 h-6 text-indigo-600 dark:text-indigo-400" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-base text-gray-900 dark:text-gray-100">Take Product Tour</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Learn about RowBoat's features
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
<a
|
||||
href="https://docs.rowboatlabs.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block"
|
||||
>
|
||||
<Button
|
||||
className="w-full justify-start gap-4 text-left py-6 px-4 hover:bg-indigo-50 dark:hover:bg-indigo-500/10 transition-all duration-200 group hover:scale-[1.02] hover:shadow-md"
|
||||
variant="light"
|
||||
>
|
||||
<div className="bg-indigo-100 dark:bg-indigo-500/20 p-2 rounded-lg group-hover:bg-indigo-200 dark:group-hover:bg-indigo-500/30 transition-colors">
|
||||
<BookOpen className="w-6 h-6 text-indigo-600 dark:text-indigo-400" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-base text-gray-900 dark:text-gray-100">View Documentation</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Read our detailed guides
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://discord.gg/gtbGcqF4"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block"
|
||||
>
|
||||
<Button
|
||||
className="w-full justify-start gap-4 text-left py-6 px-4 hover:bg-indigo-50 dark:hover:bg-indigo-500/10 transition-all duration-200 group hover:scale-[1.02] hover:shadow-md"
|
||||
variant="light"
|
||||
>
|
||||
<div className="bg-indigo-100 dark:bg-indigo-500/20 p-2 rounded-lg group-hover:bg-indigo-200 dark:group-hover:bg-indigo-500/30 transition-colors">
|
||||
<MessageCircle className="w-6 h-6 text-indigo-600 dark:text-indigo-400" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium text-base text-gray-900 dark:text-gray-100">Join Discord</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Get help from the community
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex justify-end">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300 px-4 py-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800/50 transition-colors"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -12,32 +12,42 @@ const TOUR_STEPS: TourStep[] = [
|
|||
{
|
||||
target: 'copilot',
|
||||
content: 'Build agents with the help of copilot.\nThis might take a minute.',
|
||||
title: 'Step 1/6'
|
||||
title: 'Step 1/8'
|
||||
},
|
||||
{
|
||||
target: 'playground',
|
||||
content: 'Test your assistant in the playground.\nDebug tool calls and responses.',
|
||||
title: 'Step 2/6'
|
||||
title: 'Step 2/8'
|
||||
},
|
||||
{
|
||||
target: 'entity-agents',
|
||||
content: 'Manage your agents.\nSpecify instructions, examples and tool usage.',
|
||||
title: 'Step 3/6'
|
||||
title: 'Step 3/8'
|
||||
},
|
||||
{
|
||||
target: 'entity-tools',
|
||||
content: 'Create your own tools, import MCP tools or use existing ones.\nMock tools for quick testing.',
|
||||
title: 'Step 4/6'
|
||||
title: 'Step 4/8'
|
||||
},
|
||||
{
|
||||
target: 'entity-prompts',
|
||||
content: 'Manage prompts which will be used by agents.\nConfigure greeting message.',
|
||||
title: 'Step 5/6'
|
||||
title: 'Step 5/8'
|
||||
},
|
||||
{
|
||||
target: 'settings',
|
||||
content: 'Configure project settings\nGet API keys, configure tool webhooks.',
|
||||
title: 'Step 6/6'
|
||||
title: 'Step 6/8'
|
||||
},
|
||||
{
|
||||
target: 'deploy',
|
||||
content: 'Deploy your workflow version to make it live.\nThis will make your workflow available for use via the API and SDK.\n\nLearn more:\n• <a href="https://docs.rowboatlabs.com/using_the_api/" target="_blank" class="text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300">Using the API</a>\n• <a href="https://docs.rowboatlabs.com/using_the_sdk/" target="_blank" class="text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300">Using the SDK</a>',
|
||||
title: 'Step 7/8'
|
||||
},
|
||||
{
|
||||
target: 'tour-button',
|
||||
content: 'Come back here anytime to restart the tour.\nStill have questions? See our <a href="https://docs.rowboatlabs.com/" target="_blank" class="text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300">docs</a> or reach out on <a href="https://discord.gg/gtbGcqF4" target="_blank" class="text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300">discord</a>.',
|
||||
title: 'Step 8/8'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -222,9 +232,9 @@ export function ProductTour({
|
|||
<div className="text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">
|
||||
{TOUR_STEPS[currentStep].title}
|
||||
</div>
|
||||
<div className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3 whitespace-pre-line">
|
||||
{TOUR_STEPS[currentStep].content}
|
||||
</div>
|
||||
<div className="text-sm font-medium text-gray-900 dark:text-gray-100 mb-3 whitespace-pre-line [&>a]:underline"
|
||||
dangerouslySetInnerHTML={{ __html: TOUR_STEPS[currentStep].content }}
|
||||
/>
|
||||
<div className="flex justify-between items-center">
|
||||
<button
|
||||
onClick={handleSkip}
|
||||
|
|
|
|||
|
|
@ -1,131 +0,0 @@
|
|||
import { z } from "zod";
|
||||
export const WorkflowAgent = z.object({
|
||||
name: z.string(),
|
||||
type: z.union([
|
||||
z.literal('conversation'),
|
||||
z.literal('post_process'),
|
||||
z.literal('escalation'),
|
||||
]),
|
||||
description: z.string(),
|
||||
disabled: z.boolean().default(false).optional(),
|
||||
instructions: z.string(),
|
||||
examples: z.string().optional(),
|
||||
model: z.union([
|
||||
z.literal('gpt-4o'),
|
||||
z.literal('gpt-4o-mini'),
|
||||
]),
|
||||
locked: z.boolean().default(false).describe('Whether this agent is locked and cannot be deleted').optional(),
|
||||
toggleAble: z.boolean().default(true).describe('Whether this agent can be enabled or disabled').optional(),
|
||||
global: z.boolean().default(false).describe('Whether this agent is a global agent, in which case it cannot be connected to other agents').optional(),
|
||||
ragDataSources: z.array(z.string()).optional(),
|
||||
ragReturnType: z.union([z.literal('chunks'), z.literal('content')]).default('chunks'),
|
||||
ragK: z.number().default(3),
|
||||
controlType: z.union([z.literal('retain'), z.literal('relinquish_to_parent'), z.literal('relinquish_to_start')]).default('retain').describe('Whether this agent retains control after a turn, relinquishes to the parent agent, or relinquishes to the start agent'),
|
||||
});
|
||||
export const WorkflowPrompt = z.object({
|
||||
name: z.string(),
|
||||
type: z.union([
|
||||
z.literal('base_prompt'),
|
||||
z.literal('style_prompt'),
|
||||
z.literal('greeting'),
|
||||
]),
|
||||
prompt: z.string(),
|
||||
});
|
||||
export const WorkflowTool = z.object({
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
mockTool: z.boolean().default(false).optional(),
|
||||
autoSubmitMockedResponse: z.boolean().default(false).optional(),
|
||||
mockInstructions: z.string().optional(),
|
||||
parameters: z.object({
|
||||
type: z.literal('object'),
|
||||
properties: z.record(z.object({
|
||||
type: z.string(),
|
||||
description: z.string(),
|
||||
})),
|
||||
required: z.array(z.string()).optional(),
|
||||
}),
|
||||
isMcp: z.boolean().default(false).optional(),
|
||||
mcpServerName: z.string().optional(),
|
||||
});
|
||||
export const Workflow = z.object({
|
||||
name: z.string().optional(),
|
||||
agents: z.array(WorkflowAgent),
|
||||
prompts: z.array(WorkflowPrompt),
|
||||
tools: z.array(WorkflowTool),
|
||||
startAgent: z.string(),
|
||||
createdAt: z.string().datetime(),
|
||||
lastUpdatedAt: z.string().datetime(),
|
||||
projectId: z.string(),
|
||||
});
|
||||
export const WorkflowTemplate = Workflow
|
||||
.omit({
|
||||
projectId: true,
|
||||
lastUpdatedAt: true,
|
||||
createdAt: true,
|
||||
})
|
||||
.extend({
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
});
|
||||
|
||||
export const ConnectedEntity = z.object({
|
||||
type: z.union([z.literal('tool'), z.literal('prompt'), z.literal('agent')]),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
export function sanitizeTextWithMentions(
|
||||
text: string,
|
||||
workflow: {
|
||||
agents: z.infer<typeof WorkflowAgent>[],
|
||||
tools: z.infer<typeof WorkflowTool>[],
|
||||
prompts: z.infer<typeof WorkflowPrompt>[],
|
||||
},
|
||||
): {
|
||||
sanitized: string;
|
||||
entities: z.infer<typeof ConnectedEntity>[];
|
||||
} {
|
||||
// Regex to match [@type:name](#type:something) pattern where type is tool/prompt/agent
|
||||
const mentionRegex = /\[@(tool|prompt|agent):([^\]]+)\]\(#mention\)/g;
|
||||
const seen = new Set<string>();
|
||||
|
||||
// collect entities
|
||||
const entities = Array
|
||||
.from(text.matchAll(mentionRegex))
|
||||
.filter(match => {
|
||||
if (seen.has(match[0])) {
|
||||
return false;
|
||||
}
|
||||
seen.add(match[0]);
|
||||
return true;
|
||||
})
|
||||
.map(match => {
|
||||
return {
|
||||
type: match[1] as 'tool' | 'prompt' | 'agent',
|
||||
name: match[2],
|
||||
};
|
||||
})
|
||||
.filter(entity => {
|
||||
seen.add(entity.name);
|
||||
if (entity.type === 'agent') {
|
||||
return workflow.agents.some(a => a.name === entity.name);
|
||||
} else if (entity.type === 'tool') {
|
||||
return workflow.tools.some(t => t.name === entity.name);
|
||||
} else if (entity.type === 'prompt') {
|
||||
return workflow.prompts.some(p => p.name === entity.name);
|
||||
}
|
||||
return false;
|
||||
})
|
||||
|
||||
// sanitize text
|
||||
for (const entity of entities) {
|
||||
const id = `${entity.type}:${entity.name}`;
|
||||
const textToReplace = `[@${id}](#mention)`;
|
||||
text = text.replace(textToReplace, `[@${id}]`);
|
||||
}
|
||||
|
||||
return {
|
||||
sanitized: text,
|
||||
entities,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import traceback
|
||||
from quart import Quart, request, jsonify, Response
|
||||
from datetime import datetime
|
||||
from functools import wraps
|
||||
import os
|
||||
import json
|
||||
|
|
@ -8,15 +7,19 @@ from hypercorn.config import Config
|
|||
from hypercorn.asyncio import serve
|
||||
import asyncio
|
||||
|
||||
from src.graph.core import run_turn, run_turn_streamed
|
||||
from src.graph.tools import RAG_TOOL, CLOSE_CHAT_TOOL
|
||||
from src.utils.common import common_logger, read_json_from_file
|
||||
from src.graph.core import run_turn_streamed
|
||||
from src.utils.common import read_json_from_file
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
logger = common_logger
|
||||
app = Quart(__name__)
|
||||
config = read_json_from_file("./configs/default_config.json")
|
||||
master_config = read_json_from_file("./configs/default_config.json")
|
||||
print("Master config:", master_config)
|
||||
|
||||
# Get environment variables with defaults
|
||||
ENABLE_TRACING = False
|
||||
try:
|
||||
ENABLE_TRACING = os.environ.get('ENABLE_TRACING').lower() == 'true'
|
||||
except Exception as e:
|
||||
print(f"Error getting ENABLE_TRACING: {e}, using default of False")
|
||||
|
||||
# filter out agent transfer messages using a function
|
||||
def is_agent_transfer_message(msg):
|
||||
|
|
@ -59,8 +62,8 @@ def require_api_key(f):
|
|||
@app.route("/chat", methods=["POST"])
|
||||
@require_api_key
|
||||
async def chat():
|
||||
logger.info('='*100)
|
||||
logger.info(f"{'*'*100}Running server mode{'*'*100}")
|
||||
print('='*100)
|
||||
print(f"{'*'*100}Running server mode{'*'*100}")
|
||||
try:
|
||||
request_data = await request.get_json()
|
||||
print("Request:", json.dumps(request_data))
|
||||
|
|
@ -84,7 +87,6 @@ async def chat():
|
|||
data = request_data
|
||||
messages = []
|
||||
final_state = {}
|
||||
# tokens_used = 0
|
||||
|
||||
async for event_type, event_data in run_turn_streamed(
|
||||
messages=input_messages,
|
||||
|
|
@ -92,32 +94,31 @@ async def chat():
|
|||
agent_configs=data.get("agents", []),
|
||||
tool_configs=data.get("tools", []),
|
||||
prompt_configs=data.get("prompts", []),
|
||||
start_turn_with_start_agent=config.get("start_turn_with_start_agent", False),
|
||||
start_turn_with_start_agent=master_config.get("start_turn_with_start_agent", False),
|
||||
state=data.get("state", {}),
|
||||
additional_tool_configs=[RAG_TOOL, CLOSE_CHAT_TOOL],
|
||||
complete_request=data
|
||||
complete_request=data,
|
||||
enable_tracing=ENABLE_TRACING
|
||||
):
|
||||
if event_type == 'message':
|
||||
messages.append(event_data)
|
||||
elif event_type == 'done':
|
||||
final_state = event_data['state']
|
||||
# tokens_used = event_data["tokens_used"]
|
||||
|
||||
out = {
|
||||
"messages": messages,
|
||||
"state": final_state,
|
||||
}
|
||||
|
||||
logger.info("Output:")
|
||||
print("Output:")
|
||||
for k, v in out.items():
|
||||
logger.info(f"{k}: {v}")
|
||||
logger.info('*'*100)
|
||||
print(f"{k}: {v}")
|
||||
print('*'*100)
|
||||
|
||||
return jsonify(out)
|
||||
|
||||
except Exception as e:
|
||||
print(traceback.format_exc())
|
||||
logger.error(f"Error: {str(e)}")
|
||||
print(f"Error: {str(e)}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
def format_sse(data: dict, event: str = None) -> str:
|
||||
|
|
@ -152,6 +153,7 @@ async def chat_stream():
|
|||
msg["role"] = "user"
|
||||
|
||||
async def generate():
|
||||
print("Running generate() in server")
|
||||
try:
|
||||
async for event_type, event_data in run_turn_streamed(
|
||||
messages=input_messages,
|
||||
|
|
@ -159,23 +161,20 @@ async def chat_stream():
|
|||
agent_configs=request_data.get("agents", []),
|
||||
tool_configs=request_data.get("tools", []),
|
||||
prompt_configs=request_data.get("prompts", []),
|
||||
start_turn_with_start_agent=config.get("start_turn_with_start_agent", False),
|
||||
start_turn_with_start_agent=master_config.get("start_turn_with_start_agent", False),
|
||||
state=request_data.get("state", {}),
|
||||
additional_tool_configs=[RAG_TOOL, CLOSE_CHAT_TOOL],
|
||||
complete_request=request_data
|
||||
complete_request=request_data,
|
||||
enable_tracing=ENABLE_TRACING
|
||||
):
|
||||
if event_type == 'message':
|
||||
print("Yielding message:")
|
||||
yield format_sse(event_data, "message")
|
||||
elif event_type == 'done':
|
||||
print("Yielding done:")
|
||||
yield format_sse(event_data, "done")
|
||||
elif event_type == 'error':
|
||||
print("Yielding error:")
|
||||
yield format_sse(event_data, "stream_error")
|
||||
yield format_sse(event_data, " error")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Streaming error: {str(e)}")
|
||||
print(f"Streaming error: {str(e)}")
|
||||
yield format_sse({"error": str(e)}, "error")
|
||||
|
||||
return Response(generate(), mimetype='text/event-stream')
|
||||
|
|
|
|||
|
|
@ -7,20 +7,16 @@ import logging
|
|||
from .helpers.access import (
|
||||
get_agent_by_name,
|
||||
get_external_tools,
|
||||
get_prompt_by_type
|
||||
get_prompt_by_type,
|
||||
get_agent_config_by_name
|
||||
)
|
||||
from .helpers.state import (
|
||||
construct_state_from_response
|
||||
)
|
||||
from .helpers.control import get_latest_assistant_msg, get_latest_non_assistant_messages, get_last_agent_name
|
||||
from .swarm_wrapper import run as swarm_run, run_streamed as swarm_run_streamed, create_response, get_agents
|
||||
from src.utils.common import common_logger as logger
|
||||
from .helpers.library_tools import handle_web_search_event
|
||||
from .helpers.control import get_last_agent_name
|
||||
from .execute_turn import run_streamed as swarm_run_streamed, get_agents
|
||||
from .helpers.instructions import add_child_transfer_related_instructions
|
||||
from .types import PromptType, outputVisibility, ResponseType
|
||||
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
|
||||
|
||||
from .types import PromptType
|
||||
|
||||
# Create a dedicated logger for swarm wrapper
|
||||
logger.setLevel(logging.INFO)
|
||||
print("Logger level set to INFO")
|
||||
|
||||
def order_messages(messages):
|
||||
"""
|
||||
|
|
@ -52,197 +48,41 @@ def set_sys_message(messages):
|
|||
if messages[0].get("role") == "system" and messages[0].get("content") == "":
|
||||
messages[0]["content"] = "You are a helpful assistant."
|
||||
print("Updated system message: ", messages[0])
|
||||
logger.info("Updated system message: ", messages[0])
|
||||
print("Messages: ", messages)
|
||||
# logger.info("Messages: ", messages)
|
||||
|
||||
|
||||
return messages
|
||||
|
||||
def clean_up_history(agent_data):
|
||||
"""
|
||||
Ensures each agent's history is sorted using order_messages.
|
||||
"""
|
||||
for data in agent_data:
|
||||
data["history"] = order_messages(data["history"])
|
||||
return agent_data
|
||||
def add_child_transfer_related_instructions_to_agents(agents):
|
||||
for agent in agents:
|
||||
add_child_transfer_related_instructions(agent)
|
||||
return agents
|
||||
|
||||
def create_final_response(response, turn_messages, tokens_used, all_agents):
|
||||
"""
|
||||
Constructs the final response data (messages, tokens_used, updated state) that a caller would need.
|
||||
"""
|
||||
# Ensure response has a messages attribute
|
||||
if not hasattr(response, 'messages'):
|
||||
response.messages = []
|
||||
def add_openai_recommended_instructions_to_agents(agents):
|
||||
for agent in agents:
|
||||
agent.instructions = RECOMMENDED_PROMPT_PREFIX + '\n\n' + agent.instructions
|
||||
return agents
|
||||
|
||||
# Assign the appropriate messages to the response
|
||||
response.messages = turn_messages
|
||||
def check_internal_visibility(current_agent):
|
||||
"""Check if an agent is internal based on its outputVisibility"""
|
||||
return current_agent.output_visibility == outputVisibility.INTERNAL.value
|
||||
|
||||
# Ensure tokens_used is a valid dictionary
|
||||
if not isinstance(tokens_used, dict):
|
||||
tokens_used = {"total": 0, "prompt": 0, "completion": 0} # Default values if not a dictionary
|
||||
def add_sender_details_to_messages(messages):
|
||||
for msg in messages:
|
||||
msg['sender'] = msg.get('sender', None)
|
||||
if msg.get('sender'):
|
||||
msg['content'] = f"Sender agent: {msg.get('sender')}\nContent: {msg.get('content')}"
|
||||
return messages
|
||||
|
||||
# Ensure response has a tokens_used attribute that's a dictionary
|
||||
if not hasattr(response, 'tokens_used') or not isinstance(response.tokens_used, dict):
|
||||
response.tokens_used = {}
|
||||
def append_messages(messages, accumulated_messages):
|
||||
# Create a set of existing message contents for O(1) lookup
|
||||
existing_contents = {msg.get('content') for msg in messages}
|
||||
|
||||
response.tokens_used = tokens_used
|
||||
# Append messages that aren't already present, preserving order
|
||||
for msg in accumulated_messages:
|
||||
if msg.get('content') not in existing_contents:
|
||||
messages.append(msg)
|
||||
existing_contents.add(msg.get('content'))
|
||||
|
||||
# Ensure response has an agent attribute for state construction
|
||||
if not hasattr(response, 'agent'):
|
||||
if all_agents and len(all_agents) > 0:
|
||||
response.agent = all_agents[0] # Set default agent if missing
|
||||
|
||||
new_state = construct_state_from_response(response, all_agents)
|
||||
return response.messages, response.tokens_used, new_state
|
||||
|
||||
|
||||
async def run_turn(
|
||||
messages, start_agent_name, agent_configs, tool_configs, start_turn_with_start_agent, state={}, additional_tool_configs=[], complete_request={}
|
||||
):
|
||||
"""
|
||||
Coordinates a single 'turn' of conversation or processing among agents.
|
||||
Includes validation, agent setup, optional greeting logic, error handling, and post-processing steps.
|
||||
"""
|
||||
logger.info("Running stateless turn")
|
||||
print("Running stateless turn")
|
||||
|
||||
# Sort messages by the specified ordering
|
||||
#messages = order_messages(messages)
|
||||
|
||||
# Merge any additional tool configs
|
||||
tool_configs = tool_configs + additional_tool_configs
|
||||
|
||||
# Determine if this is a greeting turn
|
||||
greeting_turn = not any(msg.get("role") != "system" for msg in messages)
|
||||
turn_messages = []
|
||||
# Initialize tokens_used as a dictionary
|
||||
tokens_used = {"total": 0, "prompt": 0, "completion": 0}
|
||||
|
||||
agent_data = state.get("agent_data", [])
|
||||
|
||||
# If not a greeting turn, localize the last user or system messages
|
||||
if not greeting_turn:
|
||||
latest_assistant_msg = get_latest_assistant_msg(messages)
|
||||
latest_non_assistant_msgs = get_latest_non_assistant_messages(messages)
|
||||
msg_type = latest_non_assistant_msgs[-1]["role"]
|
||||
|
||||
# Determine the last agent from state/config
|
||||
last_agent_name = get_last_agent_name(
|
||||
state=state,
|
||||
agent_configs=agent_configs,
|
||||
start_agent_name=start_agent_name,
|
||||
msg_type=msg_type,
|
||||
latest_assistant_msg=latest_assistant_msg,
|
||||
start_turn_with_start_agent=start_turn_with_start_agent
|
||||
)
|
||||
else:
|
||||
# For a greeting turn, we assume the last agent is the start_agent_name
|
||||
last_agent_name = start_agent_name
|
||||
|
||||
state["agent_data"] = agent_data
|
||||
|
||||
# Initialize all agents
|
||||
logger.info("Initializing agents")
|
||||
print("Initializing agents")
|
||||
new_agents = get_agents(
|
||||
agent_configs=agent_configs,
|
||||
tool_configs=tool_configs,
|
||||
complete_request=complete_request
|
||||
)
|
||||
# Prepare escalation agent
|
||||
last_new_agent = get_agent_by_name(last_agent_name, new_agents)
|
||||
|
||||
# Gather external tools for Swarm
|
||||
external_tools = get_external_tools(tool_configs)
|
||||
logger.info(f"Found {len(external_tools)} external tools")
|
||||
print(f"Found {len(external_tools)} external tools")
|
||||
|
||||
# If no validation error yet, proceed with the main run
|
||||
|
||||
logger.info("Running swarm run")
|
||||
print("Running swarm run")
|
||||
|
||||
response = await swarm_run(
|
||||
agent=last_new_agent,
|
||||
messages=messages,
|
||||
external_tools=external_tools,
|
||||
tokens_used=tokens_used
|
||||
)
|
||||
|
||||
logger.info("Swarm run completed")
|
||||
print("Swarm run completed")
|
||||
|
||||
# Initialize response.messages if it doesn't exist
|
||||
if not hasattr(response, 'messages'):
|
||||
response.messages = []
|
||||
|
||||
# Convert the ResponseOutputMessage to a standard message format
|
||||
if hasattr(response, 'new_items') and response.new_items and hasattr(response.new_items[-1], 'raw_item'):
|
||||
raw_item = response.new_items[-1].raw_item
|
||||
# Extract text content from ResponseOutputText objects
|
||||
content = ""
|
||||
if hasattr(raw_item, 'content') and raw_item.content:
|
||||
for content_item in raw_item.content:
|
||||
if hasattr(content_item, 'text'):
|
||||
content += content_item.text
|
||||
|
||||
# Create a standard message dictionary
|
||||
standard_message = {
|
||||
"role": raw_item.role if hasattr(raw_item, 'role') else "assistant",
|
||||
"content": content,
|
||||
"sender": last_new_agent.name,
|
||||
"created_at": None,
|
||||
"response_type": "internal"
|
||||
}
|
||||
|
||||
# Add the converted message to response messages
|
||||
response.messages.append(standard_message)
|
||||
|
||||
logger.info("Converted message added to response messages")
|
||||
print("Converted message added to response messages")
|
||||
|
||||
# Use a dictionary for tokens_used
|
||||
tokens_used = {"total": 0, "prompt": 0, "completion": 0} # Default values as placeholders
|
||||
|
||||
# Ensure turn_messages can be extended with response.messages
|
||||
if hasattr(response, 'messages') and isinstance(response.messages, list):
|
||||
turn_messages.extend(response.messages)
|
||||
|
||||
logger.info(f"Completed run of agent: {last_new_agent.name}")
|
||||
print(f"Completed run of agent: {last_new_agent.name}")
|
||||
|
||||
# Otherwise, duplicate the last response as external
|
||||
logger.info("No post-processing agent found. Duplicating last response and setting to external.")
|
||||
print("No post-processing agent found. Duplicating last response and setting to external.")
|
||||
if turn_messages:
|
||||
duplicate_msg = deepcopy(turn_messages[-1])
|
||||
duplicate_msg["response_type"] = "external"
|
||||
duplicate_msg["sender"] += " >> External"
|
||||
|
||||
# Ensure tokens_used remains a proper dictionary
|
||||
if not isinstance(tokens_used, dict):
|
||||
tokens_used = {"total": 0, "prompt": 0, "completion": 0} # Default values if not a dictionary
|
||||
|
||||
response = create_response(
|
||||
messages=[duplicate_msg],
|
||||
tokens_used=tokens_used,
|
||||
agent=last_new_agent,
|
||||
error_msg=''
|
||||
)
|
||||
|
||||
# Ensure response has messages attribute
|
||||
if hasattr(response, 'messages') and isinstance(response.messages, list):
|
||||
turn_messages.extend(response.messages)
|
||||
|
||||
# Finalize the response
|
||||
logger.info("Finalizing response")
|
||||
print("Finalizing response")
|
||||
return create_final_response(
|
||||
response=response,
|
||||
turn_messages=turn_messages,
|
||||
tokens_used=tokens_used,
|
||||
all_agents=new_agents
|
||||
)
|
||||
return messages
|
||||
|
||||
async def run_turn_streamed(
|
||||
messages,
|
||||
|
|
@ -252,18 +92,39 @@ async def run_turn_streamed(
|
|||
prompt_configs,
|
||||
start_turn_with_start_agent,
|
||||
state={},
|
||||
additional_tool_configs=[],
|
||||
complete_request={}
|
||||
complete_request={},
|
||||
enable_tracing=None
|
||||
):
|
||||
"""
|
||||
Run a turn of the conversation with streaming responses.
|
||||
|
||||
A turn consists of all messages between user inputs and must follow these rules:
|
||||
1. Each turn must have exactly one external message from an agent with external visibility
|
||||
2. A turn can have multiple internal messages from internal agents
|
||||
3. Each agent can output at most one regular message per parent
|
||||
4. Control flows from parent to child, and child must return to parent after responding
|
||||
5. Turn ends when an external agent outputs a message
|
||||
"""
|
||||
print("\n=== Starting new turn ===")
|
||||
print(f"Starting agent: {start_agent_name}")
|
||||
|
||||
# Use enable_tracing from complete_request if available, otherwise default to False
|
||||
enable_tracing = complete_request.get("enable_tracing", False) if enable_tracing is None else enable_tracing
|
||||
|
||||
messages = set_sys_message(messages)
|
||||
messages = add_sender_details_to_messages(messages)
|
||||
is_greeting_turn = not any(msg.get("role") != "system" for msg in messages)
|
||||
final_state = None # Initialize outside try block
|
||||
final_state = None
|
||||
accumulated_messages = []
|
||||
agent_message_counts = {} # Track messages per agent
|
||||
child_call_counts = {} # Track parent->child calls
|
||||
current_agent = None
|
||||
parent_stack = []
|
||||
|
||||
try:
|
||||
greeting_prompt = get_prompt_by_type(prompt_configs, PromptType.GREETING)
|
||||
# Handle greeting turn
|
||||
if is_greeting_turn:
|
||||
if not greeting_prompt:
|
||||
greeting_prompt = "How can I help you today?"
|
||||
print("Greeting prompt not found. Using default: ", greeting_prompt)
|
||||
greeting_prompt = get_prompt_by_type(prompt_configs, PromptType.GREETING) or "How can I help you today?"
|
||||
message = {
|
||||
'content': greeting_prompt,
|
||||
'role': 'assistant',
|
||||
|
|
@ -271,21 +132,33 @@ async def run_turn_streamed(
|
|||
'tool_calls': None,
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': 'external'
|
||||
'response_type': ResponseType.EXTERNAL.value
|
||||
}
|
||||
print("Yielding greeting message: ", message)
|
||||
accumulated_messages.append(message)
|
||||
print('-'*100)
|
||||
print(f"Yielding message: {message}")
|
||||
print('-'*100)
|
||||
yield ('message', message)
|
||||
|
||||
final_state = {
|
||||
"last_agent_name": start_agent_name if start_agent_name else None,
|
||||
"tokens": {"total": 0, "prompt": 0, "completion": 0}
|
||||
"last_agent_name": start_agent_name,
|
||||
"tokens": {"total": 0, "prompt": 0, "completion": 0},
|
||||
"turn_messages": accumulated_messages
|
||||
}
|
||||
print("Yielding done message")
|
||||
print('-'*100)
|
||||
print(f"Yielding done: {final_state}")
|
||||
print('-'*100)
|
||||
yield ('done', {'state': final_state})
|
||||
return
|
||||
|
||||
|
||||
# Initialize agents and get external tools
|
||||
new_agents = get_agents(agent_configs=agent_configs, tool_configs=tool_configs, complete_request=complete_request)
|
||||
|
||||
new_agents = get_agents(
|
||||
agent_configs=agent_configs,
|
||||
tool_configs=tool_configs,
|
||||
complete_request=complete_request
|
||||
)
|
||||
new_agents = add_child_transfer_related_instructions_to_agents(new_agents)
|
||||
new_agents = add_openai_recommended_instructions_to_agents(new_agents)
|
||||
last_agent_name = get_last_agent_name(
|
||||
state=state,
|
||||
agent_configs=agent_configs,
|
||||
|
|
@ -294,368 +167,319 @@ async def run_turn_streamed(
|
|||
latest_assistant_msg=None,
|
||||
start_turn_with_start_agent=start_turn_with_start_agent
|
||||
)
|
||||
last_new_agent = get_agent_by_name(last_agent_name, new_agents)
|
||||
current_agent = get_agent_by_name(last_agent_name, new_agents)
|
||||
external_tools = get_external_tools(tool_configs)
|
||||
|
||||
current_agent = last_new_agent
|
||||
tokens_used = {"total": 0, "prompt": 0, "completion": 0}
|
||||
iter = 0
|
||||
while True:
|
||||
iter += 1
|
||||
is_internal_agent = check_internal_visibility(current_agent)
|
||||
print('-'*100)
|
||||
print(f"Iteration {iter} of turn loop")
|
||||
print(f"Current agent: {current_agent.name} (internal: {is_internal_agent})")
|
||||
print(f"Parent stack: {[agent.name for agent in parent_stack]}")
|
||||
print('-'*100)
|
||||
|
||||
stream_result = await swarm_run_streamed(
|
||||
agent=last_new_agent,
|
||||
messages=messages,
|
||||
external_tools=external_tools,
|
||||
tokens_used=tokens_used
|
||||
)
|
||||
messages = append_messages(messages, accumulated_messages)
|
||||
# Run the current agent
|
||||
stream_result = await swarm_run_streamed(
|
||||
agent=current_agent,
|
||||
messages=messages,
|
||||
external_tools=external_tools,
|
||||
tokens_used=tokens_used,
|
||||
enable_tracing=enable_tracing
|
||||
)
|
||||
|
||||
# Process streaming events
|
||||
async for event in stream_result.stream_events():
|
||||
print('='*50)
|
||||
print("Received event: ", event)
|
||||
print('-'*50)
|
||||
async for event in stream_result.stream_events():
|
||||
try:
|
||||
# Handle web search events
|
||||
if event.type == "raw_response_event":
|
||||
# Handle token usage counting
|
||||
if hasattr(event.data, 'type') and event.data.type == "response.completed" and hasattr(event.data.response, 'usage'):
|
||||
tokens_used["total"] += event.data.response.usage.total_tokens
|
||||
tokens_used["prompt"] += event.data.response.usage.input_tokens
|
||||
tokens_used["completion"] += event.data.response.usage.output_tokens
|
||||
print('-'*50)
|
||||
print(f"Found usage information. Updated cumulative tokens: {tokens_used}")
|
||||
print('-'*50)
|
||||
|
||||
# Handle raw response events and accumulate tokens
|
||||
if event.type == "raw_response_event":
|
||||
if hasattr(event.data, 'type') and event.data.type == "response.completed" and event.data.response.usage:
|
||||
if hasattr(event.data.response, 'usage'):
|
||||
tokens_used["total"] += event.data.response.usage.total_tokens
|
||||
tokens_used["prompt"] += event.data.response.usage.input_tokens
|
||||
tokens_used["completion"] += event.data.response.usage.output_tokens
|
||||
print('-'*50)
|
||||
print(f"Found usage information. Updated cumulative tokens: {tokens_used}")
|
||||
print('-'*50)
|
||||
web_search_messages = handle_web_search_event(event, current_agent)
|
||||
for message in web_search_messages:
|
||||
message['response_type'] = ResponseType.INTERNAL.value
|
||||
print('-'*100)
|
||||
print(f"Yielding message: {message}")
|
||||
print('-'*100)
|
||||
yield ('message', message)
|
||||
if message.get('role') != 'tool':
|
||||
message['content'] = f"Sender agent: {current_agent.name}\nContent: {message['content']}"
|
||||
accumulated_messages.append(message)
|
||||
continue
|
||||
|
||||
# Handle ResponseFunctionWebSearch specifically
|
||||
if hasattr(event, 'data') and hasattr(event.data, 'raw_item'):
|
||||
raw_item = event.data.raw_item
|
||||
# Handle agent transfer
|
||||
elif event.type == "agent_updated_stream_event":
|
||||
|
||||
# Check if it's a web search call
|
||||
if (hasattr(raw_item, 'type') and raw_item.type == 'web_search_call') or (
|
||||
isinstance(raw_item, dict) and raw_item.get('type') == 'web_search_call'
|
||||
):
|
||||
# Get call_id safely, regardless of structure
|
||||
call_id = None
|
||||
if hasattr(raw_item, 'id'):
|
||||
call_id = raw_item.id
|
||||
elif isinstance(raw_item, dict) and 'id' in raw_item:
|
||||
call_id = raw_item['id']
|
||||
else:
|
||||
call_id = str(uuid.uuid4())
|
||||
# Skip self-transfers
|
||||
if current_agent.name == event.new_agent.name:
|
||||
print(f"\nSkipping agent transfer attempt: {current_agent.name} -> {event.new_agent.name} (self-transfer)")
|
||||
continue
|
||||
|
||||
# Get status safely
|
||||
status = 'unknown'
|
||||
if hasattr(raw_item, 'status'):
|
||||
status = raw_item.status
|
||||
elif isinstance(raw_item, dict) and 'status' in raw_item:
|
||||
status = raw_item['status']
|
||||
# Check if we've already called this child agent too many times
|
||||
parent_child_key = f"{current_agent.name}:{event.new_agent.name}"
|
||||
current_count = child_call_counts.get(parent_child_key, 0)
|
||||
if current_count >= event.new_agent.max_calls_per_parent_agent:
|
||||
print(f"Skipping transfer from {current_agent.name} to {event.new_agent.name} (max calls reached from parent to child)")
|
||||
continue
|
||||
|
||||
# Emit a tool call for web search
|
||||
# Transfer to new agent
|
||||
tool_call_id = str(uuid.uuid4())
|
||||
message = {
|
||||
'content': None,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name if current_agent else None,
|
||||
'sender': current_agent.name,
|
||||
'tool_calls': [{
|
||||
'function': {
|
||||
'name': 'web_search',
|
||||
'name': 'transfer_to_agent',
|
||||
'arguments': json.dumps({
|
||||
'search_id': call_id,
|
||||
'status': status
|
||||
'assistant': event.new_agent.name
|
||||
})
|
||||
},
|
||||
'id': call_id,
|
||||
'id': tool_call_id,
|
||||
'type': 'function'
|
||||
}],
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': 'internal'
|
||||
'response_type': ResponseType.INTERNAL.value
|
||||
}
|
||||
print("Yielding web search raw response message: ", message)
|
||||
print('-'*100)
|
||||
print(f"Yielding message: {message}")
|
||||
print('-'*100)
|
||||
yield ('message', message)
|
||||
|
||||
continue
|
||||
|
||||
# Update current agent when it changes
|
||||
elif event.type == "agent_updated_stream_event":
|
||||
if current_agent.name == event.new_agent.name:
|
||||
continue
|
||||
|
||||
tool_call_id = str(uuid.uuid4())
|
||||
|
||||
# yield the transfer invocation
|
||||
message = {
|
||||
'content': None,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name,
|
||||
'tool_calls': [{
|
||||
'function': {
|
||||
'name': 'transfer_to_agent',
|
||||
'arguments': json.dumps({
|
||||
# Record transfer result
|
||||
message = {
|
||||
'content': json.dumps({
|
||||
'assistant': event.new_agent.name
|
||||
})
|
||||
},
|
||||
'id': tool_call_id,
|
||||
'type': 'function'
|
||||
}],
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': 'internal'
|
||||
}
|
||||
print("Yielding message: ", message)
|
||||
yield ('message', message)
|
||||
|
||||
# yield the transfer result
|
||||
message = {
|
||||
'content': json.dumps({
|
||||
'assistant': event.new_agent.name
|
||||
}),
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': tool_call_id,
|
||||
'tool_name': 'transfer_to_agent',
|
||||
}
|
||||
print("Yielding message: ", message)
|
||||
yield ('message', message)
|
||||
|
||||
current_agent = event.new_agent
|
||||
continue
|
||||
|
||||
# Handle run items (tools, messages, etc)
|
||||
elif event.type == "run_item_stream_event":
|
||||
current_agent = event.item.agent
|
||||
if event.item.type == "tool_call_item":
|
||||
# Check if it's a ResponseFunctionWebSearch object
|
||||
if hasattr(event.item.raw_item, 'type') and event.item.raw_item.type == 'web_search_call':
|
||||
call_id = event.item.raw_item.id if hasattr(event.item.raw_item, 'id') else str(uuid.uuid4())
|
||||
message = {
|
||||
'content': None,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name if current_agent else None,
|
||||
'tool_calls': [{
|
||||
'function': {
|
||||
'name': 'web_search',
|
||||
'arguments': json.dumps({
|
||||
'search_id': call_id
|
||||
})
|
||||
},
|
||||
'id': call_id,
|
||||
'type': 'function'
|
||||
}],
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': 'internal'
|
||||
}
|
||||
print("Yielding message: ", message)
|
||||
yield ('message', message)
|
||||
|
||||
result_message = {
|
||||
'content': "Web search done",
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': call_id,
|
||||
'tool_name': 'web_search',
|
||||
'response_type': 'internal'
|
||||
}
|
||||
|
||||
print("Yielding web search results: ", result_message)
|
||||
yield ('message', result_message)
|
||||
else:
|
||||
# Handle normal tool calls
|
||||
message = {
|
||||
'content': None,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name if current_agent else None,
|
||||
'tool_calls': [{
|
||||
'function': {
|
||||
'name': event.item.raw_item.name,
|
||||
'arguments': event.item.raw_item.arguments
|
||||
},
|
||||
'id': event.item.raw_item.call_id,
|
||||
'type': 'function'
|
||||
}],
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': 'internal'
|
||||
}
|
||||
print("Yielding message: ", message)
|
||||
yield ('message', message)
|
||||
|
||||
|
||||
elif event.item.type == "tool_call_output_item":
|
||||
# Check if it's a web search result
|
||||
if isinstance(event.item.raw_item, dict) and event.item.raw_item.get('type') == 'web_search_results':
|
||||
call_id = event.item.raw_item.get('search_id', event.item.raw_item.get('id', str(uuid.uuid4())))
|
||||
message = {
|
||||
'content': str(event.item.output),
|
||||
}),
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': call_id,
|
||||
'tool_name': 'web_search',
|
||||
'response_type': 'internal'
|
||||
'tool_call_id': tool_call_id,
|
||||
'tool_name': 'transfer_to_agent'
|
||||
}
|
||||
else:
|
||||
# Safe extraction of call_id and name
|
||||
call_id = None
|
||||
tool_name = None
|
||||
print('-'*100)
|
||||
print(f"Yielding message: {message}")
|
||||
print('-'*100)
|
||||
yield ('message', message)
|
||||
|
||||
# Handle different types of raw_item
|
||||
if isinstance(event.item.raw_item, dict):
|
||||
call_id = event.item.raw_item.get('call_id')
|
||||
tool_name = event.item.raw_item.get('name')
|
||||
elif hasattr(event.item.raw_item, 'call_id'):
|
||||
call_id = event.item.raw_item.call_id
|
||||
# Update tracking and switch to child
|
||||
if check_internal_visibility(event.new_agent):
|
||||
child_call_counts[parent_child_key] = current_count + 1
|
||||
parent_stack.append(current_agent)
|
||||
current_agent = event.new_agent
|
||||
|
||||
# Handle regular messages and tool calls
|
||||
elif event.type == "run_item_stream_event":
|
||||
if event.item.type == "tool_call_item":
|
||||
# Check if it's a web search call
|
||||
if hasattr(event.item.raw_item, 'type') and event.item.raw_item.type == 'web_search_call':
|
||||
web_search_messages = handle_web_search_event(event, current_agent)
|
||||
for message in web_search_messages:
|
||||
message['response_type'] = ResponseType.INTERNAL.value
|
||||
print('-'*100)
|
||||
print(f"Yielding message: {message}")
|
||||
print('-'*100)
|
||||
yield ('message', message)
|
||||
if message.get('role') != 'tool':
|
||||
message['content'] = f"Sender agent: {current_agent.name}\nContent: {message['content']}"
|
||||
accumulated_messages.append(message)
|
||||
continue
|
||||
|
||||
# Handle regular tool calls
|
||||
message = {
|
||||
'content': None,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name,
|
||||
'tool_calls': [{
|
||||
'function': {
|
||||
'name': event.item.raw_item.name,
|
||||
'arguments': event.item.raw_item.arguments
|
||||
},
|
||||
'id': event.item.raw_item.call_id,
|
||||
'type': 'function'
|
||||
}],
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': ResponseType.INTERNAL.value
|
||||
}
|
||||
print('-'*100)
|
||||
print(f"Yielding message: {message}")
|
||||
print('-'*100)
|
||||
yield ('message', message)
|
||||
message['content'] = f"Sender agent: {current_agent.name}\nContent: {message['content']}"
|
||||
accumulated_messages.append(message)
|
||||
|
||||
elif event.item.type == "tool_call_output_item":
|
||||
# Get the tool name and call id from raw_item
|
||||
tool_call_id = None
|
||||
tool_name = None
|
||||
|
||||
# Try to get call_id from various possible locations
|
||||
if hasattr(event.item.raw_item, 'call_id'):
|
||||
tool_call_id = event.item.raw_item.call_id
|
||||
elif isinstance(event.item.raw_item, dict) and 'call_id' in event.item.raw_item:
|
||||
tool_call_id = event.item.raw_item['call_id']
|
||||
|
||||
# Try to get tool name from various possible locations
|
||||
if hasattr(event.item.raw_item, 'name'):
|
||||
tool_name = event.item.raw_item.name
|
||||
elif isinstance(event.item.raw_item, dict):
|
||||
if 'name' in event.item.raw_item:
|
||||
tool_name = event.item.raw_item['name']
|
||||
elif 'type' in event.item.raw_item and event.item.raw_item['type'] == 'function_call_output':
|
||||
# For function call outputs, try to infer from context
|
||||
tool_name = 'recommendation' # Default for function calls
|
||||
|
||||
message = {
|
||||
'content': str(event.item.output),
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': call_id,
|
||||
'tool_name': tool_name,
|
||||
'response_type': 'internal'
|
||||
}
|
||||
# Fallback to event item if available
|
||||
if not tool_name and hasattr(event.item, 'tool_name'):
|
||||
tool_name = event.item.tool_name
|
||||
if not tool_call_id and hasattr(event.item, 'tool_call_id'):
|
||||
tool_call_id = event.item.tool_call_id
|
||||
|
||||
print("Yielding message: ", message)
|
||||
yield ('message', message)
|
||||
message = {
|
||||
'content': str(event.item.output),
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': tool_call_id,
|
||||
'tool_name': tool_name,
|
||||
'response_type': ResponseType.INTERNAL.value
|
||||
}
|
||||
print('-'*100)
|
||||
print(f"Yielding tool call output message: {message}")
|
||||
print('-'*100)
|
||||
yield ('message', message)
|
||||
|
||||
elif event.item.type == "message_output_item":
|
||||
content = ""
|
||||
url_citations = []
|
||||
elif event.item.type == "message_output_item":
|
||||
# Extract content and citations
|
||||
content = ""
|
||||
url_citations = []
|
||||
if hasattr(event.item.raw_item, 'content'):
|
||||
for content_item in event.item.raw_item.content:
|
||||
if hasattr(content_item, 'text'):
|
||||
content += content_item.text
|
||||
if hasattr(content_item, 'annotations'):
|
||||
for annotation in content_item.annotations:
|
||||
if hasattr(annotation, 'type') and annotation.type == 'url_citation':
|
||||
citation = {
|
||||
'url': annotation.url if hasattr(annotation, 'url') else '',
|
||||
'title': annotation.title if hasattr(annotation, 'title') else '',
|
||||
'start_index': annotation.start_index if hasattr(annotation, 'start_index') else 0,
|
||||
'end_index': annotation.end_index if hasattr(annotation, 'end_index') else 0
|
||||
}
|
||||
url_citations.append(citation)
|
||||
|
||||
# Extract text content and any URL citations
|
||||
if hasattr(event.item.raw_item, 'content'):
|
||||
for content_item in event.item.raw_item.content:
|
||||
# Handle text content
|
||||
if hasattr(content_item, 'text'):
|
||||
content += content_item.text
|
||||
# Determine message type and create message
|
||||
is_internal = check_internal_visibility(current_agent)
|
||||
response_type = ResponseType.INTERNAL.value if is_internal else ResponseType.EXTERNAL.value
|
||||
|
||||
# Extract URL citations if present
|
||||
if hasattr(content_item, 'annotations'):
|
||||
for annotation in content_item.annotations:
|
||||
if hasattr(annotation, 'type') and annotation.type == 'url_citation':
|
||||
citation = {
|
||||
'url': annotation.url if hasattr(annotation, 'url') else '',
|
||||
'title': annotation.title if hasattr(annotation, 'title') else '',
|
||||
'start_index': annotation.start_index if hasattr(annotation, 'start_index') else 0,
|
||||
'end_index': annotation.end_index if hasattr(annotation, 'end_index') else 0
|
||||
}
|
||||
url_citations.append(citation)
|
||||
message = {
|
||||
'content': content,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': response_type
|
||||
}
|
||||
|
||||
# Create message with URL citations if they exist
|
||||
message = {
|
||||
'content': content,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': 'external'
|
||||
}
|
||||
if url_citations:
|
||||
message['citations'] = url_citations
|
||||
|
||||
# Add citations if any were found
|
||||
if url_citations:
|
||||
message['citations'] = url_citations
|
||||
# Track that this agent has responded
|
||||
if not message.get('tool_calls'): # If there are no tool calls, it's a content response
|
||||
agent_message_counts[current_agent.name] = 1
|
||||
print('-'*100)
|
||||
print(f"Yielding message: {message}")
|
||||
print('-'*100)
|
||||
yield ('message', message)
|
||||
message['content'] = f"Sender agent: {current_agent.name}\nContent: {message['content']}"
|
||||
accumulated_messages.append(message)
|
||||
# Return to parent or end turn
|
||||
if is_internal and parent_stack:
|
||||
# Create tool call for control transition
|
||||
tool_call_id = str(uuid.uuid4())
|
||||
transition_message = {
|
||||
'content': None,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name,
|
||||
'tool_calls': [{
|
||||
'function': {
|
||||
'name': 'transfer_to_agent',
|
||||
'arguments': json.dumps({
|
||||
'assistant': parent_stack[-1].name
|
||||
})
|
||||
},
|
||||
'id': tool_call_id,
|
||||
'type': 'function'
|
||||
}],
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': ResponseType.INTERNAL.value
|
||||
}
|
||||
print('-'*100)
|
||||
print(f"Yielding control transition message: {transition_message}")
|
||||
print('-'*100)
|
||||
yield ('message', transition_message)
|
||||
|
||||
print("Yielding message: ", message)
|
||||
yield ('message', message)
|
||||
# Create tool response for control transition
|
||||
transition_response = {
|
||||
'content': json.dumps({
|
||||
'assistant': parent_stack[-1].name
|
||||
}),
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': tool_call_id,
|
||||
'tool_name': 'transfer_to_agent'
|
||||
}
|
||||
print('-'*100)
|
||||
print(f"Yielding control transition response: {transition_response}")
|
||||
print('-'*100)
|
||||
yield ('message', transition_response)
|
||||
|
||||
# Handle web search function call events
|
||||
elif event.item.type == "web_search_call_item" or (hasattr(event.item, 'raw_item') and hasattr(event.item.raw_item, 'type') and event.item.raw_item.type == 'web_search_call'):
|
||||
# Extract web search call ID if available
|
||||
call_id = None
|
||||
if hasattr(event.item.raw_item, 'id'):
|
||||
call_id = event.item.raw_item.id
|
||||
current_agent = parent_stack.pop()
|
||||
continue
|
||||
elif not is_internal:
|
||||
break
|
||||
|
||||
message = {
|
||||
'content': None,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name if current_agent else None,
|
||||
'tool_calls': [{
|
||||
'function': {
|
||||
'name': 'web_search',
|
||||
'arguments': json.dumps({
|
||||
'search_id': call_id
|
||||
})
|
||||
},
|
||||
'id': call_id or str(uuid.uuid4()),
|
||||
'type': 'function'
|
||||
}],
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': 'internal'
|
||||
}
|
||||
print("Yielding web search message: ", message)
|
||||
yield ('message', message)
|
||||
except Exception as e:
|
||||
print("\n=== Error in stream event processing ===")
|
||||
print(f"Error: {str(e)}")
|
||||
print("Event details:")
|
||||
print(f"Event type: {event.type if hasattr(event, 'type') else 'unknown'}")
|
||||
if hasattr(event, '__dict__'):
|
||||
print(f"Event attributes: {event.__dict__}")
|
||||
print(f"Full event object: {event}")
|
||||
print(f"Traceback: {traceback.format_exc()}")
|
||||
print("=" * 50)
|
||||
raise
|
||||
|
||||
# Handle web search results
|
||||
elif event.item.type == "web_search_results_item" or (
|
||||
hasattr(event.item, 'raw_item') and (
|
||||
(hasattr(event.item.raw_item, 'type') and event.item.raw_item.type == 'web_search_results') or
|
||||
(isinstance(event.item.raw_item, dict) and event.item.raw_item.get('type') == 'web_search_results')
|
||||
)
|
||||
):
|
||||
# Extract call_id safely
|
||||
call_id = None
|
||||
raw_item = event.item.raw_item
|
||||
# Break main loop if we've output an external message
|
||||
if not is_internal_agent and current_agent.name in agent_message_counts:
|
||||
break
|
||||
|
||||
# Try several ways to get the search_id or id
|
||||
if hasattr(raw_item, 'search_id'):
|
||||
call_id = raw_item.search_id
|
||||
elif isinstance(raw_item, dict) and 'search_id' in raw_item:
|
||||
call_id = raw_item['search_id']
|
||||
elif hasattr(raw_item, 'id'):
|
||||
call_id = raw_item.id
|
||||
elif isinstance(raw_item, dict) and 'id' in raw_item:
|
||||
call_id = raw_item['id']
|
||||
else:
|
||||
call_id = str(uuid.uuid4())
|
||||
|
||||
# Extract results content safely
|
||||
results = {}
|
||||
|
||||
# Try event.item.output first
|
||||
if hasattr(event.item, 'output'):
|
||||
results = event.item.output
|
||||
# Then try raw_item.results
|
||||
elif hasattr(raw_item, 'results'):
|
||||
results = raw_item.results
|
||||
elif isinstance(raw_item, dict) and 'results' in raw_item:
|
||||
results = raw_item['results']
|
||||
|
||||
# Format the results for output
|
||||
results_str = ""
|
||||
try:
|
||||
results_str = json.dumps(results) if results else ""
|
||||
except Exception as e:
|
||||
print(f"Error serializing results: {str(e)}")
|
||||
results_str = str(results)
|
||||
|
||||
message = {
|
||||
'content': results_str,
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': call_id,
|
||||
'tool_name': 'web_search',
|
||||
'response_type': 'internal'
|
||||
}
|
||||
print("Yielding web search results: ", message)
|
||||
yield ('message', message)
|
||||
|
||||
print(f"\n{'='*50}\n")
|
||||
|
||||
# After all events are processed, set final state
|
||||
# Set final state
|
||||
final_state = {
|
||||
"last_agent_name": current_agent.name if current_agent else None,
|
||||
"tokens": tokens_used
|
||||
"tokens": tokens_used,
|
||||
"turn_messages": accumulated_messages
|
||||
}
|
||||
print('-'*100)
|
||||
print(f"Yielding done: {final_state}")
|
||||
print('-'*100)
|
||||
yield ('done', {'state': final_state})
|
||||
|
||||
except Exception as e:
|
||||
print(traceback.format_exc())
|
||||
print(f"Error in stream processing: {str(e)}")
|
||||
print("Yielding error event:", {'error': str(e), 'state': final_state})
|
||||
yield ('error', {'error': str(e), 'state': final_state}) # Include final_state in error response
|
||||
yield ('error', {'error': str(e), 'state': final_state})
|
||||
|
|
@ -3,7 +3,7 @@ import json
|
|||
import aiohttp
|
||||
import jwt
|
||||
import hashlib
|
||||
from agents import OpenAIChatCompletionsModel
|
||||
from agents import OpenAIChatCompletionsModel, trace, add_trace_processor
|
||||
|
||||
# Import helper functions needed for get_agents
|
||||
from .helpers.access import (
|
||||
|
|
@ -13,12 +13,12 @@ from .helpers.access import (
|
|||
from .helpers.instructions import (
|
||||
add_rag_instructions_to_agent
|
||||
)
|
||||
|
||||
from .types import outputVisibility
|
||||
from agents import Agent as NewAgent, Runner, FunctionTool, RunContextWrapper, ModelSettings, WebSearchTool
|
||||
from .tracing import AgentTurnTraceProcessor
|
||||
# Add import for OpenAI functionality
|
||||
from src.utils.common import common_logger as logger, generate_openai_output
|
||||
from src.utils.common import generate_openai_output
|
||||
from typing import Any
|
||||
from dataclasses import asdict
|
||||
import asyncio
|
||||
from mcp import ClientSession
|
||||
from mcp.client.sse import sse_client
|
||||
|
|
@ -54,7 +54,7 @@ async def mock_tool(tool_name: str, args: str, description: str, mock_instructio
|
|||
response_content = generate_openai_output(messages, output_type='text', model=PROVIDER_DEFAULT_MODEL)
|
||||
return response_content
|
||||
except Exception as e:
|
||||
logger.error(f"Error in mock_tool: {str(e)}")
|
||||
print(f"Error in mock_tool: {str(e)}")
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
async def call_webhook(tool_name: str, args: str, webhook_url: str, signing_secret: str) -> str:
|
||||
|
|
@ -91,7 +91,7 @@ async def call_webhook(tool_name: str, args: str, webhook_url: str, signing_secr
|
|||
print(f"Webhook error: {error_msg}")
|
||||
return f"Error: {error_msg}"
|
||||
except Exception as e:
|
||||
logger.error(f"Exception in call_webhook: {str(e)}")
|
||||
print(f"Exception in call_webhook: {str(e)}")
|
||||
return f"Error: Failed to call webhook - {str(e)}"
|
||||
|
||||
async def call_mcp(tool_name: str, args: str, mcp_server_url: str) -> str:
|
||||
|
|
@ -106,7 +106,7 @@ async def call_mcp(tool_name: str, args: str, mcp_server_url: str) -> str:
|
|||
|
||||
return json_output
|
||||
except Exception as e:
|
||||
logger.error(f"Error in call_mcp: {str(e)}")
|
||||
print(f"Error in call_mcp: {str(e)}")
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
async def catch_all(ctx: RunContextWrapper[Any], args: str, tool_name: str, tool_config: dict, complete_request: dict) -> str:
|
||||
|
|
@ -143,7 +143,7 @@ async def catch_all(ctx: RunContextWrapper[Any], args: str, tool_name: str, tool
|
|||
response_content = await call_webhook(tool_name, args, webhook_url, signing_secret)
|
||||
return response_content
|
||||
except Exception as e:
|
||||
logger.error(f"Error in catch_all: {str(e)}")
|
||||
print(f"Error in catch_all: {str(e)}")
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
|
||||
|
|
@ -176,6 +176,8 @@ def get_rag_tool(config: dict, complete_request: dict) -> FunctionTool:
|
|||
return tool
|
||||
else:
|
||||
return None
|
||||
|
||||
DEFAULT_MAX_CALLS_PER_PARENT_AGENT = 3
|
||||
|
||||
def get_agents(agent_configs, tool_configs, complete_request):
|
||||
"""
|
||||
|
|
@ -191,7 +193,6 @@ def get_agents(agent_configs, tool_configs, complete_request):
|
|||
new_agent_name_to_index = {}
|
||||
# Create Agent objects from config
|
||||
for agent_config in agent_configs:
|
||||
logger.debug(f"Processing config for agent: {agent_config['name']}")
|
||||
print("="*100)
|
||||
print(f"Processing config for agent: {agent_config['name']}")
|
||||
|
||||
|
|
@ -204,14 +205,12 @@ def get_agents(agent_configs, tool_configs, complete_request):
|
|||
# Prepare tool lists for this agent
|
||||
external_tools = []
|
||||
|
||||
logger.debug(f"Agent {agent_config['name']} has {len(agent_config['tools'])} configured tools")
|
||||
print(f"Agent {agent_config['name']} has {len(agent_config['tools'])} configured tools")
|
||||
|
||||
new_tools = []
|
||||
rag_tool = get_rag_tool(agent_config, complete_request)
|
||||
if rag_tool:
|
||||
new_tools.append(rag_tool)
|
||||
logger.debug(f"Added rag tool to agent {agent_config['name']}")
|
||||
print(f"Added rag tool to agent {agent_config['name']}")
|
||||
|
||||
for tool_name in agent_config["tools"]:
|
||||
|
|
@ -235,22 +234,22 @@ def get_agents(agent_configs, tool_configs, complete_request):
|
|||
catch_all(ctx, args, _tool_name, _tool_config, _complete_request)
|
||||
)
|
||||
new_tools.append(tool)
|
||||
logger.debug(f"Added tool {tool_name} to agent {agent_config['name']}")
|
||||
print(f"Added tool {tool_name} to agent {agent_config['name']}")
|
||||
else:
|
||||
logger.warning(f"Tool {tool_name} not found in tool_configs")
|
||||
print(f"WARNING: Tool {tool_name} not found in tool_configs")
|
||||
|
||||
# Create the agent object
|
||||
logger.debug(f"Creating Agent object for {agent_config['name']}")
|
||||
print(f"Creating Agent object for {agent_config['name']}")
|
||||
|
||||
# add the name and description to the agent instructions
|
||||
agent_instructions = f"## Your Name\n{agent_config['name']}\n\n## Description\n{agent_config['description']}\n\n## Instructions\n{agent_config['instructions']}"
|
||||
try:
|
||||
# Identify the model
|
||||
model_name = agent_config["model"] if agent_config["model"] else PROVIDER_DEFAULT_MODEL
|
||||
print(f"Using model: {model_name}")
|
||||
model=OpenAIChatCompletionsModel(model=model_name, openai_client=client) if client else agent_config["model"]
|
||||
|
||||
# Create the agent object
|
||||
new_agent = NewAgent(
|
||||
name=agent_config["name"],
|
||||
instructions=agent_instructions,
|
||||
|
|
@ -260,13 +259,26 @@ def get_agents(agent_configs, tool_configs, complete_request):
|
|||
model_settings=ModelSettings(temperature=0.0)
|
||||
)
|
||||
|
||||
# Set the max calls per parent agent
|
||||
new_agent.max_calls_per_parent_agent = agent_config.get("maxCallsPerParentAgent", DEFAULT_MAX_CALLS_PER_PARENT_AGENT)
|
||||
if not agent_config.get("maxCallsPerParentAgent", None):
|
||||
print(f"WARNING: Max calls per parent agent not received for agent {new_agent.name}. Using rowboat_agents default of {DEFAULT_MAX_CALLS_PER_PARENT_AGENT}")
|
||||
else:
|
||||
print(f"Max calls per parent agent for agent {new_agent.name}: {new_agent.max_calls_per_parent_agent}")
|
||||
|
||||
# Set output visibility
|
||||
new_agent.output_visibility = agent_config.get("outputVisibility", outputVisibility.EXTERNAL.value)
|
||||
if not agent_config.get("outputVisibility", None):
|
||||
print(f"WARNING: Output visibility not received for agent {new_agent.name}. Using rowboat_agents default of {new_agent.output_visibility}")
|
||||
else:
|
||||
print(f"Output visibility for agent {new_agent.name}: {new_agent.output_visibility}")
|
||||
|
||||
# Handle the connected agents
|
||||
new_agent_to_children[agent_config["name"]] = agent_config.get("connectedAgents", [])
|
||||
new_agent_name_to_index[agent_config["name"]] = len(new_agents)
|
||||
new_agents.append(new_agent)
|
||||
logger.debug(f"Successfully created agent: {agent_config['name']}")
|
||||
print(f"Successfully created agent: {agent_config['name']}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create agent {agent_config['name']}: {str(e)}")
|
||||
print(f"ERROR: Failed to create agent {agent_config['name']}: {str(e)}")
|
||||
raise
|
||||
|
||||
|
|
@ -276,96 +288,25 @@ def get_agents(agent_configs, tool_configs, complete_request):
|
|||
new_agent.handoffs = []
|
||||
# Look up the agent's children from the old agent and create a list called handoffs in new_agent with pointers to the children in new_agents
|
||||
new_agent.handoffs = [new_agents[new_agent_name_to_index[child]] for child in new_agent_to_children[new_agent.name]]
|
||||
|
||||
|
||||
print("Returning created agents")
|
||||
print("="*100)
|
||||
return new_agents
|
||||
|
||||
|
||||
def create_response(messages=None, tokens_used=None, agent=None, error_msg=''):
|
||||
"""
|
||||
Create a Response object with the given parameters.
|
||||
|
||||
Args:
|
||||
messages: List of messages
|
||||
tokens_used: Dictionary tracking token usage
|
||||
agent: The agent that generated the response
|
||||
error_msg: Error message if any
|
||||
|
||||
Returns:
|
||||
Response object
|
||||
"""
|
||||
if messages is None:
|
||||
messages = []
|
||||
if tokens_used is None:
|
||||
tokens_used = {}
|
||||
|
||||
return NewResponse(
|
||||
messages=messages,
|
||||
agent=agent,
|
||||
tokens_used=tokens_used,
|
||||
error_msg=error_msg
|
||||
)
|
||||
|
||||
|
||||
async def run(
|
||||
agent,
|
||||
messages,
|
||||
external_tools=None,
|
||||
tokens_used=None
|
||||
):
|
||||
"""
|
||||
Wrapper function for initializing and running the Swarm client.
|
||||
"""
|
||||
logger.info(f"Initializing Swarm client for agent: {agent.name}")
|
||||
print(f"Initializing Swarm client for agent: {agent.name}")
|
||||
|
||||
# Initialize default parameters
|
||||
if external_tools is None:
|
||||
external_tools = []
|
||||
if tokens_used is None:
|
||||
tokens_used = {}
|
||||
|
||||
# Format messages to ensure they're compatible with the OpenAI API
|
||||
formatted_messages = []
|
||||
for msg in messages:
|
||||
if isinstance(msg, dict) and "content" in msg:
|
||||
formatted_msg = {
|
||||
"role": msg.get("role", "user"),
|
||||
"content": msg["content"]
|
||||
}
|
||||
formatted_messages.append(formatted_msg)
|
||||
else:
|
||||
formatted_messages.append({
|
||||
"role": "user",
|
||||
"content": str(msg)
|
||||
})
|
||||
|
||||
logger.info("Beginning Swarm run")
|
||||
print("Beginning Swarm run")
|
||||
|
||||
try:
|
||||
response = await Runner.run(agent, formatted_messages)
|
||||
except Exception as e:
|
||||
logger.error(f"Error during run: {str(e)}")
|
||||
print(f"Error during run: {str(e)}")
|
||||
raise
|
||||
|
||||
logger.info(f"Completed Swarm run for agent: {agent.name}")
|
||||
print(f"Completed Swarm run for agent: {agent.name}")
|
||||
return response
|
||||
# Initialize a flag to track if the trace processor is added
|
||||
trace_processor_added = False
|
||||
|
||||
async def run_streamed(
|
||||
agent,
|
||||
messages,
|
||||
external_tools=None,
|
||||
tokens_used=None
|
||||
tokens_used=None,
|
||||
enable_tracing=False
|
||||
):
|
||||
"""
|
||||
Wrapper function for initializing and running the Swarm client in streaming mode.
|
||||
"""
|
||||
logger.info(f"Initializing Swarm streaming client for agent: {agent.name}")
|
||||
print(f"Initializing Swarm streaming client for agent: {agent.name}")
|
||||
print(f"Initializing streaming client for agent: {agent.name}")
|
||||
|
||||
# Initialize default parameters
|
||||
if external_tools is None:
|
||||
|
|
@ -388,14 +329,39 @@ async def run_streamed(
|
|||
"content": str(msg)
|
||||
})
|
||||
|
||||
logger.info("Beginning Swarm streaming run")
|
||||
print("Beginning Swarm streaming run")
|
||||
print("Beginning streaming run")
|
||||
|
||||
try:
|
||||
# Use the Runner.run_streamed method
|
||||
# Add our custom trace processor only if tracing is enabled
|
||||
global trace_processor_added
|
||||
if enable_tracing and not trace_processor_added:
|
||||
trace_processor = AgentTurnTraceProcessor()
|
||||
add_trace_processor(trace_processor)
|
||||
trace_processor_added = True
|
||||
|
||||
# Get the stream result without trace context first
|
||||
stream_result = Runner.run_streamed(agent, formatted_messages)
|
||||
|
||||
# If tracing is enabled, wrap the stream_events to handle tracing
|
||||
if enable_tracing:
|
||||
original_stream_events = stream_result.stream_events
|
||||
|
||||
async def wrapped_stream_events():
|
||||
# Create trace context inside the async function
|
||||
with trace(f"Agent turn: {agent.name}") as trace_ctx:
|
||||
try:
|
||||
async for event in original_stream_events():
|
||||
yield event
|
||||
except GeneratorExit:
|
||||
# Handle generator exit gracefully
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Error in stream events: {str(e)}")
|
||||
raise
|
||||
|
||||
stream_result.stream_events = wrapped_stream_events
|
||||
|
||||
return stream_result
|
||||
except Exception as e:
|
||||
logger.error(f"Error during streaming run: {str(e)}")
|
||||
print(f"Error during streaming run: {str(e)}")
|
||||
raise
|
||||
|
|
@ -3,7 +3,7 @@ from src.utils.common import generate_llm_output
|
|||
import os
|
||||
import copy
|
||||
|
||||
from .swarm_wrapper import Agent, Response, create_response
|
||||
from .execute_turn import Agent, Response, create_response
|
||||
|
||||
from src.utils.common import common_logger, generate_openai_output, update_tokens_used
|
||||
logger = common_logger
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
from .access import get_agent_config_by_name, get_agent_data_by_name
|
||||
from src.graph.types import ControlType
|
||||
from src.utils.common import common_logger
|
||||
logger = common_logger
|
||||
|
||||
def get_last_agent_name(state, agent_configs, start_agent_name, msg_type, latest_assistant_msg, start_turn_with_start_agent):
|
||||
default_last_agent_name = state.get("last_agent_name", '')
|
||||
|
|
@ -9,7 +7,7 @@ def get_last_agent_name(state, agent_configs, start_agent_name, msg_type, latest
|
|||
specific_agent_data = get_agent_data_by_name(default_last_agent_name, state.get("agent_data", []))
|
||||
|
||||
# Overrides for special cases
|
||||
logger.info("Setting agent control based on last agent and control type")
|
||||
print("Setting agent control based on last agent and control type")
|
||||
if msg_type == "tool":
|
||||
last_agent_name = default_last_agent_name
|
||||
assert last_agent_name == latest_assistant_msg.get("sender", ''), "Last agent name does not match sender of latest assistant message during tool call handling"
|
||||
|
|
@ -22,7 +20,7 @@ def get_last_agent_name(state, agent_configs, start_agent_name, msg_type, latest
|
|||
if control_type == ControlType.PARENT_AGENT.value:
|
||||
last_agent_name = specific_agent_data.get("most_recent_parent_name", None) if specific_agent_data else None
|
||||
if not last_agent_name:
|
||||
logger.error("Most recent parent is empty, defaulting to same agent instead")
|
||||
print("Most recent parent is empty, defaulting to same agent instead")
|
||||
last_agent_name = default_last_agent_name
|
||||
elif control_type == ControlType.START_AGENT.value:
|
||||
last_agent_name = start_agent_name
|
||||
|
|
@ -30,7 +28,7 @@ def get_last_agent_name(state, agent_configs, start_agent_name, msg_type, latest
|
|||
last_agent_name = default_last_agent_name
|
||||
|
||||
if default_last_agent_name != last_agent_name:
|
||||
logger.info(f"Last agent name changed from {default_last_agent_name} to {last_agent_name} due to control settings")
|
||||
print(f"Last agent name changed from {default_last_agent_name} to {last_agent_name} due to control settings")
|
||||
|
||||
return last_agent_name
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from src.graph.instructions import TRANSFER_CHILDREN_INSTRUCTIONS, TRANSFER_PARENT_AWARE_INSTRUCTIONS, RAG_INSTRUCTIONS, ERROR_ESCALATION_AGENT_INSTRUCTIONS, TRANSFER_GIVE_UP_CONTROL_INSTRUCTIONS, SYSTEM_MESSAGE
|
||||
from src.graph.instructions import TRANSFER_CHILDREN_INSTRUCTIONS, TRANSFER_PARENT_AWARE_INSTRUCTIONS, RAG_INSTRUCTIONS, ERROR_ESCALATION_AGENT_INSTRUCTIONS, TRANSFER_GIVE_UP_CONTROL_INSTRUCTIONS, SYSTEM_MESSAGE, CHILD_TRANSFER_RELATED_INSTRUCTIONS
|
||||
|
||||
def add_transfer_instructions_to_parent_agents(agent, children, transfer_functions):
|
||||
other_agent_name_descriptions_tools = f'\n{'-'*100}\n'.join([f"Name: {agent.name}\nDescription: {agent.description if agent.description else ''}\nTool for transfer: {transfer_functions[agent.name].__name__}" for agent in children.values()])
|
||||
|
|
@ -36,4 +36,9 @@ def get_universal_system_message(messages):
|
|||
|
||||
def add_universal_system_message_to_agent(agent, universal_sys_msg):
|
||||
agent.instructions = agent.instructions + f'\n\n{'-'*100}\n\n' + universal_sys_msg
|
||||
return agent
|
||||
return agent
|
||||
|
||||
def add_child_transfer_related_instructions(agent):
|
||||
prompt = CHILD_TRANSFER_RELATED_INSTRUCTIONS
|
||||
agent.instructions = agent.instructions + f'\n\n{'-'*100}\n\n' + prompt
|
||||
return agent
|
||||
|
|
|
|||
268
apps/rowboat_agents/src/graph/helpers/library_tools.py
Normal file
268
apps/rowboat_agents/src/graph/helpers/library_tools.py
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
import json
|
||||
import uuid
|
||||
import traceback
|
||||
|
||||
def handle_web_search_event(event, current_agent):
|
||||
"""
|
||||
Helper function to handle all web search related events.
|
||||
Returns a list of messages to yield.
|
||||
"""
|
||||
messages = []
|
||||
|
||||
try:
|
||||
# Handle raw response web search
|
||||
if event.type == "raw_response_event":
|
||||
if hasattr(event, 'data') and hasattr(event.data, 'raw_item'):
|
||||
raw_item = event.data.raw_item
|
||||
if (hasattr(raw_item, 'type') and raw_item.type == 'web_search_call') or (
|
||||
isinstance(raw_item, dict) and raw_item.get('type') == 'web_search_call'
|
||||
):
|
||||
call_id = None
|
||||
if hasattr(raw_item, 'id'):
|
||||
call_id = raw_item.id
|
||||
elif isinstance(raw_item, dict) and 'id' in raw_item:
|
||||
call_id = raw_item['id']
|
||||
else:
|
||||
call_id = str(uuid.uuid4())
|
||||
|
||||
status = 'unknown'
|
||||
if hasattr(raw_item, 'status'):
|
||||
status = raw_item.status
|
||||
elif isinstance(raw_item, dict) and 'status' in raw_item:
|
||||
status = raw_item['status']
|
||||
tool_call_msg = {
|
||||
'content': None,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name if current_agent else None,
|
||||
'tool_calls': [{
|
||||
'function': {
|
||||
'name': 'web_search',
|
||||
'arguments': json.dumps({
|
||||
'search_id': call_id,
|
||||
'status': status
|
||||
})
|
||||
},
|
||||
'id': call_id,
|
||||
'type': 'function'
|
||||
}],
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': 'internal'
|
||||
}
|
||||
print(f"Condition for tool call matched in raw_response_event. Appending tool call message: {tool_call_msg}")
|
||||
messages.append(tool_call_msg)
|
||||
|
||||
tool_call_output_dummy_msg = {
|
||||
'content': 'Web search completed.',
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': call_id,
|
||||
'tool_name': 'web_search',
|
||||
'response_type': 'internal'
|
||||
}
|
||||
messages.append(tool_call_output_dummy_msg)
|
||||
|
||||
# Handle run item web search events
|
||||
elif event.type == "run_item_stream_event":
|
||||
if event.item.type == "tool_call_item":
|
||||
try:
|
||||
# Check if it's a web search call
|
||||
if hasattr(event.item.raw_item, 'type') and event.item.raw_item.type == 'web_search_call':
|
||||
call_id = event.item.raw_item.id if hasattr(event.item.raw_item, 'id') else str(uuid.uuid4())
|
||||
tool_call_msg = {
|
||||
'content': None,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name if current_agent else None,
|
||||
'tool_calls': [{
|
||||
'function': {
|
||||
'name': 'web_search',
|
||||
'arguments': json.dumps({
|
||||
'search_id': call_id
|
||||
})
|
||||
},
|
||||
'id': call_id,
|
||||
'type': 'function'
|
||||
}],
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': 'internal'
|
||||
}
|
||||
print(f"Condition for tool call matched in run_item_stream_event. Appending tool call message: {tool_call_msg}")
|
||||
messages.append(tool_call_msg)
|
||||
|
||||
tool_call_output_dummy_msg = {
|
||||
'content': 'Web search completed.',
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': call_id,
|
||||
'tool_name': 'web_search',
|
||||
'response_type': 'internal'
|
||||
}
|
||||
messages.append(tool_call_output_dummy_msg)
|
||||
else:
|
||||
# Handle regular tool calls
|
||||
tool_call_msg = {
|
||||
'content': None,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name if current_agent else None,
|
||||
'tool_calls': [{
|
||||
'function': {
|
||||
'name': event.item.raw_item.name,
|
||||
'arguments': event.item.raw_item.arguments
|
||||
},
|
||||
'id': event.item.raw_item.call_id,
|
||||
'type': 'function'
|
||||
}],
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': 'internal'
|
||||
}
|
||||
print(f"Condition for tool call matched in run_item_stream_event. Appending tool call message: {tool_call_msg}")
|
||||
messages.append(tool_call_msg)
|
||||
|
||||
tool_call_output_dummy_msg = {
|
||||
'content': 'Web search completed.',
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': call_id,
|
||||
'tool_name': 'web_search',
|
||||
'response_type': 'internal'
|
||||
}
|
||||
messages.append(tool_call_output_dummy_msg)
|
||||
except Exception as e:
|
||||
print("\n=== Error in tool_call_item handling ===")
|
||||
print(f"Error: {str(e)}")
|
||||
print(f"Event type: {event.type}")
|
||||
print(f"Event item type: {event.item.type}")
|
||||
print("Event details:")
|
||||
print(f"Raw item: {event.item.raw_item}")
|
||||
if hasattr(event.item.raw_item, '__dict__'):
|
||||
print(f"Raw item attributes: {event.item.raw_item.__dict__}")
|
||||
print(f"Traceback: {traceback.format_exc()}")
|
||||
print("=" * 50)
|
||||
raise
|
||||
|
||||
elif event.item.type == "tool_call_output_item":
|
||||
if isinstance(event.item.raw_item, dict) and event.item.raw_item.get('type') == 'web_search_results':
|
||||
call_id = event.item.raw_item.get('search_id', event.item.raw_item.get('id', str(uuid.uuid4())))
|
||||
tool_call_output_msg = {
|
||||
'content': str(event.item.output),
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': call_id,
|
||||
'tool_name': 'web_search',
|
||||
'response_type': 'internal'
|
||||
}
|
||||
print(f"Condition for tool call output matched in run_item_stream_event. Appending tool call output message: {tool_call_output_msg}")
|
||||
messages.append(tool_call_output_msg)
|
||||
|
||||
elif event.item.type == "web_search_call_item" or (
|
||||
hasattr(event.item, 'raw_item') and
|
||||
hasattr(event.item.raw_item, 'type') and
|
||||
event.item.raw_item.type == 'web_search_call'
|
||||
):
|
||||
call_id = None
|
||||
if hasattr(event.item.raw_item, 'id'):
|
||||
call_id = event.item.raw_item.id
|
||||
tool_call_msg = {
|
||||
'content': None,
|
||||
'role': 'assistant',
|
||||
'sender': current_agent.name if current_agent else None,
|
||||
'tool_calls': [{
|
||||
'function': {
|
||||
'name': 'web_search',
|
||||
'arguments': json.dumps({
|
||||
'search_id': call_id
|
||||
})
|
||||
},
|
||||
'id': call_id or str(uuid.uuid4()),
|
||||
'type': 'function'
|
||||
}],
|
||||
'tool_call_id': None,
|
||||
'tool_name': None,
|
||||
'response_type': 'internal'
|
||||
}
|
||||
print(f"Condition for tool call matched in run_item_stream_event. Appending tool call message: {tool_call_msg}")
|
||||
messages.append(tool_call_msg)
|
||||
tool_call_output_dummy_msg = {
|
||||
'content': 'Web search completed.',
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': call_id,
|
||||
'tool_name': 'web_search',
|
||||
'response_type': 'internal'
|
||||
}
|
||||
messages.append(tool_call_output_dummy_msg)
|
||||
|
||||
elif event.item.type == "web_search_results_item" or (
|
||||
hasattr(event.item, 'raw_item') and (
|
||||
(hasattr(event.item.raw_item, 'type') and event.item.raw_item.type == 'web_search_results') or
|
||||
(isinstance(event.item.raw_item, dict) and event.item.raw_item.get('type') == 'web_search_results')
|
||||
)
|
||||
):
|
||||
raw_item = event.item.raw_item
|
||||
call_id = None
|
||||
|
||||
if hasattr(raw_item, 'search_id'):
|
||||
call_id = raw_item.search_id
|
||||
elif isinstance(raw_item, dict) and 'search_id' in raw_item:
|
||||
call_id = raw_item['search_id']
|
||||
elif hasattr(raw_item, 'id'):
|
||||
call_id = raw_item.id
|
||||
elif isinstance(raw_item, dict) and 'id' in raw_item:
|
||||
call_id = raw_item['id']
|
||||
else:
|
||||
call_id = str(uuid.uuid4())
|
||||
|
||||
results = {}
|
||||
if hasattr(event.item, 'output'):
|
||||
results = event.item.output
|
||||
elif hasattr(raw_item, 'results'):
|
||||
results = raw_item.results
|
||||
elif isinstance(raw_item, dict) and 'results' in raw_item:
|
||||
results = raw_item['results']
|
||||
|
||||
results_str = ""
|
||||
try:
|
||||
results_str = json.dumps(results) if results else ""
|
||||
except Exception as e:
|
||||
print(f"Error serializing results: {str(e)}")
|
||||
results_str = str(results)
|
||||
|
||||
tool_call_output_msg = {
|
||||
'content': results_str,
|
||||
'role': 'tool',
|
||||
'sender': None,
|
||||
'tool_calls': None,
|
||||
'tool_call_id': call_id,
|
||||
'tool_name': 'web_search',
|
||||
'response_type': 'internal'
|
||||
}
|
||||
print(f"Condition for tool call output matched in run_item_stream_event. Appending tool call output message: {tool_call_output_msg}")
|
||||
messages.append(tool_call_output_msg)
|
||||
|
||||
except Exception as e:
|
||||
print("\n=== Error in handle_web_search_event ===")
|
||||
print(f"Error: {str(e)}")
|
||||
print(f"Event type: {event.type}")
|
||||
if hasattr(event, 'item'):
|
||||
print(f"Event item type: {event.item.type}")
|
||||
print("Event item details:")
|
||||
print(f"Raw item: {event.item.raw_item}")
|
||||
if hasattr(event.item.raw_item, '__dict__'):
|
||||
print(f"Raw item attributes: {event.item.raw_item.__dict__}")
|
||||
print(f"Traceback: {traceback.format_exc()}")
|
||||
print("=" * 50)
|
||||
raise
|
||||
|
||||
if messages:
|
||||
print("-"*100)
|
||||
print(f"Web search related messages: {messages}")
|
||||
print("-"*100)
|
||||
|
||||
return messages
|
||||
|
|
@ -67,4 +67,39 @@ The rest of the parts of the chatbot were unable to handle the chat. Hence, the
|
|||
SYSTEM_MESSAGE = f"""
|
||||
# Additional System-Wide Context or Instructions:
|
||||
{{system_message}}
|
||||
"""
|
||||
|
||||
########################
|
||||
# Instructions for non-repeat child transfer
|
||||
########################
|
||||
CHILD_TRANSFER_RELATED_INSTRUCTIONS = f"""
|
||||
# Critical Rules for Agent Transfers and Handoffs
|
||||
|
||||
- SEQUENTIAL TRANSFERS AND RESPONSES:
|
||||
1. BEFORE transferring to any agent:
|
||||
- Plan your complete sequence of needed transfers
|
||||
- Document which responses you need to collect
|
||||
|
||||
2. DURING transfers:
|
||||
- Transfer to only ONE agent at a time
|
||||
- Wait for that agent's COMPLETE response and then proceed with the next agent
|
||||
- Store the response for later use
|
||||
- Only then proceed with the next transfer
|
||||
- Never attempt parallel or simultaneous transfers
|
||||
- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent (a handoff). You must only put out 1 transfer related tool call in one output.
|
||||
|
||||
3. AFTER receiving a response:
|
||||
- Do not transfer to another agent until you've processed the current response
|
||||
- If you need to transfer to another agent, wait for your current processing to complete
|
||||
- Never transfer back to an agent that has already responded
|
||||
|
||||
- COMPLETION REQUIREMENTS:
|
||||
- Never provide final response until ALL required agents have been consulted
|
||||
- Never attempt to get multiple responses in parallel
|
||||
- If a transfer is rejected due to multiple handoffs:
|
||||
1. Complete current response processing
|
||||
2. Then retry the transfer as next in sequence
|
||||
3. Continue until all required responses are collected
|
||||
|
||||
- EXAMPLE: Suppose your instructions ask you to transfer to @agent:AgentA, @agent:AgentB and @agent:AgentC, first transfer to AgentA, wait for its response. Then transfer to AgentB, wait for its response. Then transfer to AgentC, wait for its response. Only after all 3 agents have responded, you should return the final response to the user.
|
||||
"""
|
||||
212
apps/rowboat_agents/src/graph/tracing.py
Normal file
212
apps/rowboat_agents/src/graph/tracing.py
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
from agents import TracingProcessor
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
import json
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class AgentTurnTraceProcessor(TracingProcessor):
|
||||
"""Custom trace processor to print detailed information about agent turns."""
|
||||
|
||||
def __init__(self):
|
||||
self.span_depth = {} # Track depth of each span
|
||||
self.handoff_chain = [] # Track sequence of agent handoffs
|
||||
self.message_flow = [] # Track message flow between agents
|
||||
|
||||
def _get_indent_level(self, span):
|
||||
"""Calculate indent level based on parent_id chain."""
|
||||
depth = 0
|
||||
current_id = span.parent_id
|
||||
while current_id:
|
||||
depth += 1
|
||||
current_id = self.span_depth.get(current_id)
|
||||
return depth
|
||||
|
||||
def _format_time(self, timestamp_str):
|
||||
"""Convert ISO timestamp string to formatted time string in IST timezone."""
|
||||
try:
|
||||
dt = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
|
||||
# Add 5 hours and 30 minutes for IST timezone
|
||||
dt = dt + timedelta(hours=5, minutes=30)
|
||||
return dt.strftime("%H:%M:%S.%f")[:-3]
|
||||
except (ValueError, AttributeError):
|
||||
return "00:00:00.000"
|
||||
|
||||
def _calculate_duration(self, start_str, end_str):
|
||||
"""Calculate duration between two ISO timestamp strings in seconds."""
|
||||
try:
|
||||
start = datetime.fromisoformat(start_str.replace('Z', '+00:00'))
|
||||
end = datetime.fromisoformat(end_str.replace('Z', '+00:00'))
|
||||
return (end - start).total_seconds()
|
||||
except (ValueError, AttributeError):
|
||||
return 0.0
|
||||
|
||||
def _get_span_id(self, span):
|
||||
"""Safely get span identifier."""
|
||||
for attr in ['span_id', 'id', 'trace_id']:
|
||||
if hasattr(span, attr):
|
||||
return getattr(span, attr)
|
||||
return None
|
||||
|
||||
def _print_handoff_chain(self, indent=""):
|
||||
"""Print the current handoff chain."""
|
||||
if self.handoff_chain:
|
||||
print(f"{indent}Current Handoff Chain:")
|
||||
print(f"{indent} {' -> '.join(self.handoff_chain)}")
|
||||
|
||||
def _print_message_flow(self, indent=""):
|
||||
"""Print the message flow history."""
|
||||
if self.message_flow:
|
||||
print(f"{indent}Message Flow History:")
|
||||
for msg in self.message_flow:
|
||||
print(f"{indent} {msg}")
|
||||
|
||||
def on_trace_start(self, trace):
|
||||
"""Called when a trace starts."""
|
||||
separator = "="*100
|
||||
print("\n" + separator)
|
||||
print("🚀 TRACE START")
|
||||
print(f"Name: {trace.name}")
|
||||
print(f"ID: {trace.trace_id}")
|
||||
if trace.metadata:
|
||||
print("\nMetadata:")
|
||||
for key, value in trace.metadata.items():
|
||||
print(f" {key}: {value}")
|
||||
print(separator + "\n")
|
||||
|
||||
# Reset tracking for new trace
|
||||
self.handoff_chain = []
|
||||
self.message_flow = []
|
||||
|
||||
def on_trace_end(self, trace):
|
||||
"""Called when a trace ends."""
|
||||
separator = "="*100
|
||||
print("\n" + separator)
|
||||
print("✅ TRACE END")
|
||||
print(f"Name: {trace.name}")
|
||||
print(f"ID: {trace.trace_id}")
|
||||
|
||||
# Print final chain state
|
||||
print("\nFinal State:")
|
||||
self._print_handoff_chain(" ")
|
||||
self._print_message_flow(" ")
|
||||
|
||||
print(separator + "\n")
|
||||
|
||||
# Clear tracking
|
||||
self.span_depth.clear()
|
||||
self.handoff_chain = []
|
||||
self.message_flow = []
|
||||
|
||||
def on_span_start(self, span):
|
||||
"""Called when a span starts."""
|
||||
try:
|
||||
indent = " " * self._get_indent_level(span)
|
||||
start_time = self._format_time(span.started_at)
|
||||
span_id = self._get_span_id(span)
|
||||
|
||||
# Track span depth
|
||||
if span.parent_id and span_id:
|
||||
self.span_depth[span_id] = span.parent_id
|
||||
|
||||
# Print span header with clear section separator
|
||||
print(f"\n{indent}{'>'*40}")
|
||||
print(f"{indent}▶ [{start_time}] {span.span_data.type.upper()} SPAN START")
|
||||
print(f"{indent} ID: {span_id}")
|
||||
print(f"{indent} Parent ID: {span.parent_id}")
|
||||
|
||||
data = span.span_data.export()
|
||||
|
||||
# Print span-specific information
|
||||
if span.span_data.type == "agent":
|
||||
agent_name = data.get('name', 'Unknown')
|
||||
print(f"{indent} Agent: {agent_name}")
|
||||
print(f"{indent} Handoffs: {', '.join(data.get('handoffs', []))}")
|
||||
|
||||
# Track agent in handoff chain
|
||||
if agent_name not in self.handoff_chain:
|
||||
self.handoff_chain.append(agent_name)
|
||||
self._print_handoff_chain(indent + " ")
|
||||
|
||||
elif span.span_data.type == "generation":
|
||||
print(f"{indent} Model: {data.get('model', 'Unknown')}")
|
||||
messages = data.get('messages', [])
|
||||
if messages:
|
||||
print(f"{indent} Messages: {len(messages)} message(s)")
|
||||
|
||||
elif span.span_data.type == "function":
|
||||
print(f"{indent} Function: {data.get('name', 'Unknown')}")
|
||||
args = data.get('arguments')
|
||||
if args:
|
||||
print(f"{indent} Arguments: {args}")
|
||||
|
||||
elif span.span_data.type == "handoff":
|
||||
from_agent = data.get('from_agent', 'Unknown')
|
||||
to_agent = data.get('to_agent', 'Unknown')
|
||||
print(f"{indent} From: {from_agent}")
|
||||
print(f"{indent} To: {to_agent}")
|
||||
|
||||
# Track handoff in message flow
|
||||
flow_msg = f"{from_agent} -> {to_agent}"
|
||||
self.message_flow.append(flow_msg)
|
||||
print(f"{indent} Message Flow:")
|
||||
for msg in self.message_flow[-3:]: # Show last 3 flows
|
||||
print(f"{indent} {msg}")
|
||||
|
||||
print(f"{indent}{'>'*40}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error in on_span_start: {str(e)}")
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
|
||||
def on_span_end(self, span):
|
||||
"""Called when a span ends."""
|
||||
try:
|
||||
indent = " " * self._get_indent_level(span)
|
||||
end_time = self._format_time(span.ended_at)
|
||||
duration = self._calculate_duration(span.started_at, span.ended_at)
|
||||
|
||||
# Print span end information with clear section separator
|
||||
print(f"\n{indent}{'<'*40}")
|
||||
print(f"{indent}◀ [{end_time}] {span.span_data.type.upper()} SPAN END")
|
||||
print(f"{indent} Duration: {duration:.3f}s")
|
||||
|
||||
data = span.span_data.export()
|
||||
|
||||
# Print span-specific output
|
||||
if span.span_data.type == "generation":
|
||||
output = data.get('output')
|
||||
if output:
|
||||
print(f"{indent} Output: {str(output)[:200]}...")
|
||||
|
||||
elif span.span_data.type == "function":
|
||||
output = data.get('output')
|
||||
if output:
|
||||
print(f"{indent} Output: {str(output)[:200]}...")
|
||||
|
||||
elif span.span_data.type == "handoff":
|
||||
self._print_handoff_chain(indent + " ")
|
||||
self._print_message_flow(indent + " ")
|
||||
|
||||
print(f"{indent}{'<'*40}")
|
||||
|
||||
# Clean up span depth tracking
|
||||
span_id = self._get_span_id(span)
|
||||
if span_id and span_id in self.span_depth:
|
||||
del self.span_depth[span_id]
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error in on_span_end: {str(e)}")
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
|
||||
def shutdown(self):
|
||||
"""Called when the processor is shutting down."""
|
||||
self.span_depth.clear()
|
||||
self.handoff_chain = []
|
||||
self.message_flow = []
|
||||
|
||||
def force_flush(self):
|
||||
"""Called to force flush any buffered traces/spans."""
|
||||
pass
|
||||
|
|
@ -4,10 +4,18 @@ class AgentRole(Enum):
|
|||
POST_PROCESSING = "post_process"
|
||||
GUARDRAILS = "guardrails"
|
||||
|
||||
class outputVisibility(Enum):
|
||||
EXTERNAL = "user_facing"
|
||||
INTERNAL = "internal"
|
||||
|
||||
class ResponseType(Enum):
|
||||
INTERNAL = "internal"
|
||||
EXTERNAL = "external"
|
||||
|
||||
class ControlType(Enum):
|
||||
RETAIN = "retain"
|
||||
PARENT_AGENT = "relinquish_to_parent"
|
||||
START_AGENT = "relinquish_to_start"
|
||||
START_AGENT = "start_agent"
|
||||
|
||||
class PromptType(Enum):
|
||||
STYLE = "style_prompt"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
from src.utils.common import common_logger, read_json_from_file
|
||||
from src.utils.common import read_json_from_file
|
||||
import requests
|
||||
import json
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
|
||||
logger = common_logger
|
||||
logger.info("Running app_client_streaming.py")
|
||||
print("Running app_client_streaming.py")
|
||||
|
||||
def preprocess_messages(messages):
|
||||
# Preprocess messages to handle null content and role issues
|
||||
|
|
@ -32,47 +31,12 @@ def stream_chat(host, request_data, api_key):
|
|||
print(f"Host: {host}")
|
||||
print("="*80 + "\n")
|
||||
|
||||
# First, initialize the stream
|
||||
try:
|
||||
print("\n" + "-"*80)
|
||||
print("Initializing stream...")
|
||||
init_response = requests.post(
|
||||
f"{host}/chat_stream_init",
|
||||
print("Connecting to stream...")
|
||||
stream_response = requests.post(
|
||||
f"{host}/chat_stream",
|
||||
json=request_data,
|
||||
headers={'Authorization': f'Bearer {api_key}'}
|
||||
)
|
||||
print(f"Init Response Status: {init_response.status_code}")
|
||||
print(f"Init Response Text: {init_response.text}")
|
||||
print("-"*80 + "\n")
|
||||
|
||||
if init_response.status_code != 200:
|
||||
logger.error(f"Error initializing stream. Status code: {init_response.status_code}")
|
||||
logger.error(f"Response: {init_response.text}")
|
||||
return
|
||||
|
||||
init_data = init_response.json()
|
||||
|
||||
if 'error' in init_data:
|
||||
logger.error(f"Error initializing stream: {init_data['error']}")
|
||||
return
|
||||
|
||||
stream_id = init_data['stream_id']
|
||||
print(f"Stream initialized successfully with ID: {stream_id}")
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Request error during stream initialization: {e}")
|
||||
return
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Failed to decode JSON response: {e}")
|
||||
logger.error(f"Raw response: {init_response.text}")
|
||||
return
|
||||
|
||||
# Now connect to the stream
|
||||
try:
|
||||
print("\n" + "-"*80)
|
||||
print(f"Connecting to stream {stream_id}...")
|
||||
stream_response = requests.get(
|
||||
f"{host}/chat_stream/{stream_id}",
|
||||
headers={
|
||||
'Authorization': f'Bearer {api_key}',
|
||||
'Accept': 'text/event-stream'
|
||||
|
|
@ -81,15 +45,14 @@ def stream_chat(host, request_data, api_key):
|
|||
)
|
||||
|
||||
if stream_response.status_code != 200:
|
||||
logger.error(f"Error connecting to stream. Status code: {stream_response.status_code}")
|
||||
logger.error(f"Response: {stream_response.text}")
|
||||
print(f"Error connecting to stream. Status code: {stream_response.status_code}")
|
||||
print(f"Response: {stream_response.text}")
|
||||
return
|
||||
|
||||
print(f"Successfully connected to stream")
|
||||
print("-"*80 + "\n")
|
||||
|
||||
event_count = 0
|
||||
current_data = []
|
||||
|
||||
try:
|
||||
print("\n" + "-"*80)
|
||||
|
|
@ -104,7 +67,7 @@ def stream_chat(host, request_data, api_key):
|
|||
event_data = json.loads(data)
|
||||
event_count += 1
|
||||
print("\n" + "*"*80)
|
||||
print(f"Event #{event_count}")
|
||||
print(f"Event #{event_count} at {datetime.now().isoformat()}")
|
||||
|
||||
if isinstance(event_data, dict):
|
||||
# Pretty print the event data
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ async def process_turn(messages, agent_configs, tool_configs, prompt_configs, st
|
|||
start_agent_name=start_agent_name,
|
||||
agent_configs=agent_configs,
|
||||
tool_configs=tool_configs,
|
||||
prompt_configs=prompt_configs,
|
||||
start_turn_with_start_agent=config.get("start_turn_with_start_agent", False),
|
||||
state=state,
|
||||
additional_tool_configs=[RAG_TOOL, CLOSE_CHAT_TOOL],
|
||||
|
|
|
|||
|
|
@ -1,176 +1,469 @@
|
|||
{
|
||||
"lastRequest": {
|
||||
"messages": [
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": ""
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:20:53.651Z",
|
||||
"role": "assistant",
|
||||
"content": "How can I help you today?",
|
||||
"agenticSender": "Blog Writer Hub",
|
||||
"agenticResponseType": "external"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "I want to write a blog",
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:20:58.206Z"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:20:59.319Z",
|
||||
"role": "assistant",
|
||||
"content": "Great! What topic would you like your blog to be about?",
|
||||
"agenticSender": "Blog Writer Hub",
|
||||
"agenticResponseType": "external"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Bitcoin",
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:05.048Z"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:07.311Z",
|
||||
"role": "assistant",
|
||||
"tool_calls": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": ""
|
||||
},
|
||||
"id": "62a9b5ec-3dbf-4fdd-85cb-10da37611726",
|
||||
"function": {
|
||||
"name": "transfer_to_agent",
|
||||
"arguments": "{\"assistant\": \"Research Agent\"}"
|
||||
},
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"agenticSender": "Blog Writer Hub",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:07.311Z",
|
||||
"role": "tool",
|
||||
"content": "{\"assistant\": \"Research Agent\"}",
|
||||
"tool_call_id": "62a9b5ec-3dbf-4fdd-85cb-10da37611726",
|
||||
"tool_name": "transfer_to_agent"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:10.018Z",
|
||||
"role": "assistant",
|
||||
"content": "Bitcoin is a decentralized digital currency that operates without a central authority or government, relying on blockchain technology to record transactions securely and transparently. It was created in 2009 by an anonymous person or group known as Satoshi Nakamoto, aiming to provide an alternative to traditional financial systems. Bitcoin's value is highly volatile, influenced by factors such as market demand, regulatory developments, and macroeconomic trends. It is widely used for investment, remittances, and as a means of transferring value across borders, but it also faces criticism for its association with illicit activities and environmental concerns due to energy-intensive mining processes. Despite these challenges, Bitcoin continues to gain mainstream acceptance, with increasing adoption by businesses and institutional investors.",
|
||||
"agenticSender": "Research Agent",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:10.919Z",
|
||||
"role": "assistant",
|
||||
"tool_calls": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "hi"
|
||||
},
|
||||
"id": "2085cbb1-270e-4c20-a6ef-6f9e4a3d27cf",
|
||||
"function": {
|
||||
"name": "transfer_to_agent",
|
||||
"arguments": "{\"assistant\": \"Bullet Point Agent\"}"
|
||||
},
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"agenticSender": "Blog Writer Hub",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:10.920Z",
|
||||
"role": "tool",
|
||||
"content": "{\"assistant\": \"Bullet Point Agent\"}",
|
||||
"tool_call_id": "2085cbb1-270e-4c20-a6ef-6f9e4a3d27cf",
|
||||
"tool_name": "transfer_to_agent"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:14.776Z",
|
||||
"role": "assistant",
|
||||
"content": "- Bitcoin is a decentralized digital currency that operates without a central authority or government.\n- It uses blockchain technology to securely and transparently record transactions.\n- Created in 2009 by the anonymous Satoshi Nakamoto as an alternative to traditional financial systems.\n- Bitcoin's value is highly volatile, affected by market demand, regulations, and macroeconomic trends.\n- Common uses include investment, remittances, and cross-border value transfers.\n- Criticized for links to illicit activities and environmental impact from energy-intensive mining.\n- Despite challenges, Bitcoin is gaining mainstream acceptance and adoption by businesses and institutional investors.",
|
||||
"agenticSender": "Bullet Point Agent",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:15.946Z",
|
||||
"role": "assistant",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "d48c608a-a660-451b-a3ee-eeebb1cf60ca",
|
||||
"function": {
|
||||
"name": "transfer_to_agent",
|
||||
"arguments": "{\"assistant\": \"Blog Writing Agent\"}"
|
||||
},
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"agenticSender": "Blog Writer Hub",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:15.946Z",
|
||||
"role": "tool",
|
||||
"content": "{\"assistant\": \"Blog Writing Agent\"}",
|
||||
"tool_call_id": "d48c608a-a660-451b-a3ee-eeebb1cf60ca",
|
||||
"tool_name": "transfer_to_agent"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:32.934Z",
|
||||
"role": "assistant",
|
||||
"content": "Bitcoin: Revolutionizing the World of Digital Currency\n\nIn recent years, Bitcoin has become a buzzword in financial circles and beyond. As the first and most prominent cryptocurrency, it has sparked debates, inspired innovation, and challenged traditional notions of money. But what exactly is Bitcoin, and why does it continue to capture the world’s attention? Let’s explore the key aspects that define Bitcoin and its growing influence.\n\nAt its core, Bitcoin is a decentralized digital currency that operates without the oversight of a central authority or government. This means that no single entity controls its issuance or transactions, setting it apart from traditional currencies managed by central banks. The decentralized nature of Bitcoin is one of its most revolutionary features, empowering individuals to have greater control over their finances.\n\nThe backbone of Bitcoin’s security and transparency is blockchain technology. Every transaction made with Bitcoin is recorded on a public ledger known as the blockchain. This technology ensures that transactions are secure, tamper-proof, and visible to anyone, fostering trust among users and reducing the risk of fraud.\n\nBitcoin was introduced in 2009 by an anonymous figure or group using the pseudonym Satoshi Nakamoto. The goal was to create an alternative to traditional financial systems, offering a peer-to-peer method of transferring value without intermediaries. This vision has resonated with millions, especially those seeking more autonomy over their money.\n\nOne of the most talked-about aspects of Bitcoin is its price volatility. The value of Bitcoin can fluctuate dramatically, influenced by factors such as market demand, regulatory changes, and broader economic trends. While this volatility presents opportunities for investors, it also introduces risks that potential users should carefully consider.\n\nBitcoin’s uses have evolved over time. Initially, it was seen primarily as a digital alternative to cash. Today, it is widely used for investment purposes, remittances, and transferring value across borders. Its ability to facilitate fast, low-cost international transactions has made it especially popular in regions with limited access to traditional banking services.\n\nHowever, Bitcoin is not without its critics. It has faced scrutiny for its association with illicit activities, given the pseudonymous nature of transactions. Additionally, the process of mining new bitcoins is energy-intensive, raising concerns about its environmental impact.\n\nDespite these challenges, Bitcoin’s journey toward mainstream acceptance continues. More businesses are beginning to accept Bitcoin as payment, and institutional investors are increasingly adding it to their portfolios. This growing adoption signals a shift in how society views and uses money.\n\nIn conclusion, Bitcoin represents a significant shift in the world of finance. Its decentralized structure, innovative technology, and expanding use cases make it a fascinating subject for anyone interested in the future of money. As Bitcoin continues to evolve, it will undoubtedly play a pivotal role in shaping the digital economy of tomorrow.",
|
||||
"agenticSender": "Blog Writing Agent",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:38.064Z",
|
||||
"role": "assistant",
|
||||
"content": "Here is your complete blog post on \"Bitcoin\":\n\n---\n\n**Bitcoin: Revolutionizing the World of Digital Currency**\n\nIn recent years, Bitcoin has become a buzzword in financial circles and beyond. As the first and most prominent cryptocurrency, it has sparked debates, inspired innovation, and challenged traditional notions of money. But what exactly is Bitcoin, and why does it continue to capture the world’s attention? Let’s explore the key aspects that define Bitcoin and its growing influence.\n\nAt its core, Bitcoin is a decentralized digital currency that operates without the oversight of a central authority or government. This means that no single entity controls its issuance or transactions, setting it apart from traditional currencies managed by central banks. The decentralized nature of Bitcoin is one of its most revolutionary features, empowering individuals to have greater control over their finances.\n\nThe backbone of Bitcoin’s security and transparency is blockchain technology. Every transaction made with Bitcoin is recorded on a public ledger known as the blockchain. This technology ensures that transactions are secure, tamper-proof, and visible to anyone, fostering trust among users and reducing the risk of fraud.\n\nBitcoin was introduced in 2009 by an anonymous figure or group using the pseudonym Satoshi Nakamoto. The goal was to create an alternative to traditional financial systems, offering a peer-to-peer method of transferring value without intermediaries. This vision has resonated with millions, especially those seeking more autonomy over their money.\n\nOne of the most talked-about aspects of Bitcoin is its price volatility. The value of Bitcoin can fluctuate dramatically, influenced by factors such as market demand, regulatory changes, and broader economic trends. While this volatility presents opportunities for investors, it also introduces risks that potential users should carefully consider.\n\nBitcoin’s uses have evolved over time. Initially, it was seen primarily as a digital alternative to cash. Today, it is widely used for investment purposes, remittances, and transferring value across borders. Its ability to facilitate fast, low-cost international transactions has made it especially popular in regions with limited access to traditional banking services.\n\nHowever, Bitcoin is not without its critics. It has faced scrutiny for its association with illicit activities, given the pseudonymous nature of transactions. Additionally, the process of mining new bitcoins is energy-intensive, raising concerns about its environmental impact.\n\nDespite these challenges, Bitcoin’s journey toward mainstream acceptance continues. More businesses are beginning to accept Bitcoin as payment, and institutional investors are increasingly adding it to their portfolios. This growing adoption signals a shift in how society views and uses money.\n\nIn conclusion, Bitcoin represents a significant shift in the world of finance. Its decentralized structure, innovative technology, and expanding use cases make it a fascinating subject for anyone interested in the future of money. As Bitcoin continues to evolve, it will undoubtedly play a pivotal role in shaping the digital economy of tomorrow.\n\n---\n\nIf you need any edits or want to focus on a specific aspect of Bitcoin, let me know!",
|
||||
"agenticSender": "Blog Writer Hub",
|
||||
"agenticResponseType": "external"
|
||||
}
|
||||
],
|
||||
"lastRequest": {
|
||||
"projectId": "2a4908bb-e350-4335-97df-c48615b98fe9",
|
||||
"messages": [
|
||||
{
|
||||
"content": "",
|
||||
"role": "system",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
},
|
||||
{
|
||||
"content": "How can I help you today?",
|
||||
"role": "assistant",
|
||||
"sender": "Blog Writer Hub",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
},
|
||||
{
|
||||
"content": "I want to write a blog",
|
||||
"role": "user",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
},
|
||||
{
|
||||
"content": "Great! What topic would you like your blog to be about?",
|
||||
"role": "assistant",
|
||||
"sender": "Blog Writer Hub",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
},
|
||||
{
|
||||
"content": "Bitcoin",
|
||||
"role": "user",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"last_agent_name": "Blog Writer Hub",
|
||||
"tokens": {
|
||||
"total": 0,
|
||||
"prompt": 0,
|
||||
"completion": 0
|
||||
},
|
||||
"turn_messages": [
|
||||
{
|
||||
"content": "Sender agent: Blog Writer Hub\nContent: Great! What topic would you like your blog to be about?",
|
||||
"role": "assistant",
|
||||
"sender": "Door Dash Hub",
|
||||
"content": "Hello! Are you facing issues with your order items or delivery timing? How can I assist you today?",
|
||||
"created_at": "2025-03-24T17:33:27.564940"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "my order is missing fries"
|
||||
},
|
||||
{
|
||||
"content": "Agent changed to Door Dash Hub",
|
||||
"role": "assistant",
|
||||
"sender": "Door Dash Hub",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"response_type": "internal"
|
||||
},
|
||||
{
|
||||
"content": "Agent changed to Order Issue",
|
||||
"role": "assistant",
|
||||
"sender": "Order Issue",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"response_type": "internal"
|
||||
},
|
||||
{
|
||||
"content": "I can help with that. Could you please provide your order ID so I can check the details?",
|
||||
"role": "assistant",
|
||||
"sender": "Order Issue",
|
||||
"sender": "Blog Writer Hub",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "12312"
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"last_agent_name": "Order Issue",
|
||||
"tokens": {
|
||||
"total": 1521,
|
||||
"prompt": 1486,
|
||||
"completion": 35
|
||||
}
|
||||
]
|
||||
},
|
||||
"agents": [
|
||||
{
|
||||
"name": "Blog Writer Hub",
|
||||
"type": "conversation",
|
||||
"description": "Hub agent to orchestrate the blog writing process: research, bullet points, and blog composition.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are the hub agent responsible for orchestrating the blog writing process for the user.\n\n---\n## ⚙️ Steps to Follow:\n1. Greet the user and ask for the blog topic.\n2. FIRST: Send the topic to [@agent:Research Agent] for research and wait for the summary.\n3. THEN: Send the research summary to [@agent:Bullet Point Agent] to generate bullet points and wait for the result.\n4. THEN: Send the bullet points to [@agent:Blog Writing Agent] to compose the full blog post and wait for the result.\n5. Return the final blog post to the user.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Orchestrating the blog writing process\n- Interacting with the user for topic input and delivering the final blog post\n\n❌ Out of Scope:\n- Performing research, bullet point generation, or blog writing directly\n- Handling unrelated queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Follow the strict sequence: Research → Bullet Points → Blog Writing\n- Only interact with the user for topic input and final output\n\n🚫 Don'ts:\n- Do not perform research, bullet point generation, or blog writing yourself\n- Do not try to get multiple agent responses in parallel\n- Do not reference internal agents by name to the user\n- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent (a handoff). You must only put out 1 transfer related tool call in one output.\n\n# Examples\n- **User** : I want to write a blog about 'Benefits of remote work'.\n - **Agent actions**: Call [@agent:Research Agent]\n\n- **Agent receives research summary** :\n - **Agent actions**: Call [@agent:Bullet Point Agent]\n\n- **Agent receives bullet points** :\n - **Agent actions**: Call [@agent:Blog Writing Agent]\n\n- **Agent receives blog post** :\n - **Agent response**: Here is your complete blog post on 'Benefits of remote work':\n\n[Full blog post content]\n\n- **User** : Can I get a blog on 'AI in education'?\n - **Agent actions**: Call [@agent:Research Agent](#mention)",
|
||||
"model": "gpt-4.1",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "user_facing",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": [
|
||||
"Research Agent",
|
||||
"Bullet Point Agent",
|
||||
"Blog Writing Agent"
|
||||
]
|
||||
},
|
||||
"agents": [
|
||||
{
|
||||
"name": "Research Agent",
|
||||
"type": "conversation",
|
||||
"description": "Researches and summarizes information on a given blog topic.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for researching the given blog topic and providing a concise summary of key findings.\n\n---\n## ⚙️ Steps to Follow:\n1. Use the [@tool:web_search] tool to gather information about the provided topic. This is important. You must call @tool:web_search without fail even if you know about the topic yourself.\n2. Summarize the most relevant and recent findings in 3-5 sentences.\n3. Return the summary to the calling agent.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Researching any blog topic provided\n- Summarizing findings concisely\n\n❌ Out of Scope:\n- Generating bullet points or writing the blog post\n- Interacting directly with the user\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use only reliable sources\n- Keep the summary factual and neutral\n\n🚫 Don'ts:\n- Do not include personal opinions\n- Do not generate bullet points or blog content\n- Do not preface your actions by saying things like \"I will begin by researching...\" or \"Once I have the research summary...\". Simply call the web_search tool, summarize, and return the result to the calling agent.\n- Do not interact with the user directly.\n\n# Examples\n- **User** : Research the topic 'Benefits of remote work'.\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: Remote work offers increased flexibility, improved work-life balance, and reduced commuting time. Studies show higher productivity and job satisfaction among remote employees. However, it can also lead to feelings of isolation if not managed well.\n\n- **User** : Research the topic 'AI in education'.\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: Artificial intelligence in education enables personalized learning, automates administrative tasks, and provides data-driven insights. However, it raises concerns about data privacy and the need for teacher training.",
|
||||
"model": "gpt-4.1",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "internal",
|
||||
"tools": [
|
||||
"web_search"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Bullet Point Agent",
|
||||
"type": "conversation",
|
||||
"description": "Generates key bullet points from the research summary for the blog.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for extracting and generating 5-7 key bullet points from the research summary provided.\n\n---\n## ⚙️ Steps to Follow:\n1. Read the research summary.\n2. Identify the most important facts, arguments, or insights.\n3. Generate 5-7 concise bullet points covering the main ideas.\n4. Return the bullet points to the calling agent.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Generating bullet points from research summaries\n\n❌ Out of Scope:\n- Conducting research\n- Writing the full blog post\n- Interacting directly with the user\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Ensure each bullet point is clear and self-contained\n\n🚫 Don'ts:\n- Do not repeat information\n- Do not add new information not present in the summary\n\n# Examples\n- **User** : Generate bullet points from the summary: 'Remote work offers increased flexibility, improved work-life balance, and reduced commuting time. Studies show higher productivity and job satisfaction among remote employees. However, it can also lead to feelings of isolation if not managed well.'\n - **Agent response**: \n - Increased flexibility for employees\n - Improved work-life balance\n - Reduced commuting time\n - Higher productivity among remote workers\n - Greater job satisfaction\n - Potential for isolation if not managed",
|
||||
"model": "gpt-4.1",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "internal",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Blog Writing Agent",
|
||||
"type": "conversation",
|
||||
"description": "Writes a full blog post based on provided bullet points.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for composing a well-structured blog post using the provided bullet points.\n\n---\n## ⚙️ Steps to Follow:\n1. Read the bullet points.\n2. Write an engaging introduction related to the topic.\n3. Expand on each bullet point in a logical order, creating clear paragraphs.\n4. Conclude the blog post with a summary or call to action.\n5. Return the complete blog post to the calling agent.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Writing blog posts from bullet points\n\n❌ Out of Scope:\n- Conducting research\n- Generating bullet points\n- Interacting directly with the user\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Maintain a friendly and informative tone\n- Ensure logical flow and readability\n\n🚫 Don'ts:\n- Do not add information not present in the bullet points\n- Do not interact with the user\n\n# Examples\n- **User** : Write a blog post using these bullet points: [list of bullet points]\n - **Agent response**: [A well-structured blog post expanding on each bullet point, with an introduction and conclusion]",
|
||||
"model": "gpt-4.1",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "internal",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"name": "web_search",
|
||||
"description": "Fetch information from the web based on chat context",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"isLibrary": true
|
||||
}
|
||||
],
|
||||
"prompts": [],
|
||||
"startAgent": "Blog Writer Hub",
|
||||
"mcpServers": [],
|
||||
"toolWebhookUrl": ""
|
||||
},
|
||||
"lastResponse": {
|
||||
"state": {
|
||||
"last_agent_name": "Blog Writer Hub",
|
||||
"tokens": {
|
||||
"total": 0,
|
||||
"prompt": 0,
|
||||
"completion": 0
|
||||
},
|
||||
"turn_messages": [
|
||||
{
|
||||
"name": "Door Dash Hub",
|
||||
"type": "conversation",
|
||||
"description": "Hub agent to manage Door Dash-related queries.",
|
||||
"instructions": "## \ud83e\uddd1\u200d\ud83d\udcbc Role:\nYou are responsible for directing Door Dash-related queries to appropriate agents.\n\n---\n## \u2699\ufe0f Steps to Follow:\n1. Greet the user and ask which Door Dash-related query they need help with (e.g., 'Are you facing issues with your order items or delivery timing?').\n2. If the query matches a specific task, direct the user to the corresponding agent:\n - Order Issue \u2192 [@agent:Order Issue]\n - Delayed Delivery \u2192 [@agent:Delayed Delivery]\n3. If the query doesn't match any specific task, respond with 'I'm sorry, I didn't understand. Could you clarify your request?' or escalate to human support.\n\n---\n## \ud83c\udfaf Scope:\n\u2705 In Scope:\n- Issues with order items\n- Delayed delivery issues\n\n\u274c Out of Scope:\n- Issues unrelated to Door Dash\n- General knowledge queries\n\n---\n## \ud83d\udccb Guidelines:\n\u2714\ufe0f Dos:\n- Direct queries to specific Door Dash agents promptly.\n- Call [@agent:Escalation] agent for unrecognized queries.\n\n\ud83d\udeab Don'ts:\n- Engage in detailed support.\n- Extend the conversation beyond Door Dash.\n- Provide user-facing text such as 'I will connect you now...' when calling another agent\n\n# Examples\n- **User** : I need help with my order items.\n - **Agent actions**: [@agent:Order Issue](#mention)\n\n- **User** : My delivery is delayed.\n - **Agent actions**: Call [@agent:Delayed Delivery](#mention)\n\n- **User** : I'm not sure where my order is.\n - **Agent actions**: Call [@agent:Delayed Delivery](#mention)\n\n- **User** : Can you reset my order settings?\n - **Agent actions**: [@agent:Escalation](#mention)\n\n- **User** : How are you today?\n - **Agent response**: I'm doing great. What would like help with today?",
|
||||
"model": "gpt-4o-mini",
|
||||
"hasRagSources": false,
|
||||
"controlType": "retain",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": [
|
||||
"Order Issue",
|
||||
"Delayed Delivery",
|
||||
"Escalation"
|
||||
]
|
||||
"content": "Sender agent: Research Agent\nContent: Bitcoin is a decentralized digital currency that operates without a central authority or government, relying on blockchain technology to record transactions securely and transparently. It was created in 2009 by an anonymous person or group known as Satoshi Nakamoto, aiming to provide an alternative to traditional financial systems. Bitcoin's value is highly volatile, influenced by factors such as market demand, regulatory developments, and macroeconomic trends. It is widely used for investment, remittances, and as a means of transferring value across borders, but it also faces criticism for its association with illicit activities and environmental concerns due to energy-intensive mining processes. Despite these challenges, Bitcoin continues to gain mainstream acceptance, with increasing adoption by businesses and institutional investors.",
|
||||
"role": "assistant",
|
||||
"sender": "Research Agent",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "internal"
|
||||
},
|
||||
{
|
||||
"name": "Post process",
|
||||
"type": "post_process",
|
||||
"description": "",
|
||||
"instructions": "Ensure that the agent response is terse and to the point.",
|
||||
"model": "gpt-4o-mini",
|
||||
"hasRagSources": false,
|
||||
"controlType": "retain",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
"content": "Sender agent: Bullet Point Agent\nContent: - Bitcoin is a decentralized digital currency that operates without a central authority or government.\n- It uses blockchain technology to securely and transparently record transactions.\n- Created in 2009 by the anonymous Satoshi Nakamoto as an alternative to traditional financial systems.\n- Bitcoin's value is highly volatile, affected by market demand, regulations, and macroeconomic trends.\n- Common uses include investment, remittances, and cross-border value transfers.\n- Criticized for links to illicit activities and environmental impact from energy-intensive mining.\n- Despite challenges, Bitcoin is gaining mainstream acceptance and adoption by businesses and institutional investors.",
|
||||
"role": "assistant",
|
||||
"sender": "Bullet Point Agent",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "internal"
|
||||
},
|
||||
{
|
||||
"name": "Escalation",
|
||||
"type": "escalation",
|
||||
"description": "",
|
||||
"instructions": "Get the user's contact information and let them know that their request has been escalated.",
|
||||
"model": "gpt-4o-mini",
|
||||
"hasRagSources": false,
|
||||
"controlType": "retain",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
"content": "Sender agent: Blog Writing Agent\nContent: Bitcoin: Revolutionizing the World of Digital Currency\n\nIn recent years, Bitcoin has become a buzzword in financial circles and beyond. As the first and most prominent cryptocurrency, it has sparked debates, inspired innovation, and challenged traditional notions of money. But what exactly is Bitcoin, and why does it continue to capture the world’s attention? Let’s explore the key aspects that define Bitcoin and its growing influence.\n\nAt its core, Bitcoin is a decentralized digital currency that operates without the oversight of a central authority or government. This means that no single entity controls its issuance or transactions, setting it apart from traditional currencies managed by central banks. The decentralized nature of Bitcoin is one of its most revolutionary features, empowering individuals to have greater control over their finances.\n\nThe backbone of Bitcoin’s security and transparency is blockchain technology. Every transaction made with Bitcoin is recorded on a public ledger known as the blockchain. This technology ensures that transactions are secure, tamper-proof, and visible to anyone, fostering trust among users and reducing the risk of fraud.\n\nBitcoin was introduced in 2009 by an anonymous figure or group using the pseudonym Satoshi Nakamoto. The goal was to create an alternative to traditional financial systems, offering a peer-to-peer method of transferring value without intermediaries. This vision has resonated with millions, especially those seeking more autonomy over their money.\n\nOne of the most talked-about aspects of Bitcoin is its price volatility. The value of Bitcoin can fluctuate dramatically, influenced by factors such as market demand, regulatory changes, and broader economic trends. While this volatility presents opportunities for investors, it also introduces risks that potential users should carefully consider.\n\nBitcoin’s uses have evolved over time. Initially, it was seen primarily as a digital alternative to cash. Today, it is widely used for investment purposes, remittances, and transferring value across borders. Its ability to facilitate fast, low-cost international transactions has made it especially popular in regions with limited access to traditional banking services.\n\nHowever, Bitcoin is not without its critics. It has faced scrutiny for its association with illicit activities, given the pseudonymous nature of transactions. Additionally, the process of mining new bitcoins is energy-intensive, raising concerns about its environmental impact.\n\nDespite these challenges, Bitcoin’s journey toward mainstream acceptance continues. More businesses are beginning to accept Bitcoin as payment, and institutional investors are increasingly adding it to their portfolios. This growing adoption signals a shift in how society views and uses money.\n\nIn conclusion, Bitcoin represents a significant shift in the world of finance. Its decentralized structure, innovative technology, and expanding use cases make it a fascinating subject for anyone interested in the future of money. As Bitcoin continues to evolve, it will undoubtedly play a pivotal role in shaping the digital economy of tomorrow.",
|
||||
"role": "assistant",
|
||||
"sender": "Blog Writing Agent",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "internal"
|
||||
},
|
||||
{
|
||||
"name": "Order Issue",
|
||||
"type": "conversation",
|
||||
"description": "Agent to assist users with missing or incorrect order items.",
|
||||
"instructions": "## \ud83e\uddd1\u200d\ud83d\udcbc Role:\nAssist users with issues related to missing or incorrect order items.\n\n---\n## \u2699\ufe0f Steps to Follow:\n1. Fetch the order details using the [@tool:get_order_details] tool.\n2. Confirm the issue with the user.\n3. Provide solutions or escalate if unresolved.\n\n---\n## \ud83c\udfaf Scope:\n\u2705 In Scope:\n- Handling missing or incorrect order items\n\n\u274c Out of Scope:\n- Delayed delivery issues\n- General knowledge queries\n\n---\n## \ud83d\udccb Guidelines:\n\u2714\ufe0f Dos:\n- Ensure the user is aware of the order details before proceeding.\n\n\ud83d\udeab Don'ts:\n- Extend the conversation beyond order issues.\n\n# Examples\n- **User** : I received the wrong item in my order.\n - **Agent response**: I can help with that. Let me fetch your order details first.\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : My order is missing an item.\n - **Agent response**: Let's check your order details and resolve this issue.\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : I got someone else's order.\n - **Agent response**: I apologize for the mix-up. I'll fetch your order details to sort this out.\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : Can you help me with a missing item?\n - **Agent response**: Certainly, I'll look into your order details right away.\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : There's an issue with my order items.\n - **Agent response**: Let's verify your order details to address this issue.\n - **Agent actions**: Call [@tool:get_order_details](#mention)",
|
||||
"model": "gpt-4o",
|
||||
"hasRagSources": false,
|
||||
"controlType": "retain",
|
||||
"tools": [
|
||||
"get_order_details"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Delayed Delivery",
|
||||
"type": "conversation",
|
||||
"description": "Agent to assist users with delayed delivery issues.",
|
||||
"instructions": "## \ud83e\uddd1\u200d\ud83d\udcbc Role:\nAssist users with issues related to delayed delivery.\n\n---\n## \u2699\ufe0f Steps to Follow:\n1. Fetch the delivery status using the [@tool:get_delivery_status] tool.\n2. Confirm the delay with the user.\n3. Provide solutions or escalate if unresolved.\n\n---\n## \ud83c\udfaf Scope:\n\u2705 In Scope:\n- Handling delayed delivery issues\n\n\u274c Out of Scope:\n- Missing or incorrect order items\n- General knowledge queries\n\n---\n## \ud83d\udccb Guidelines:\n\u2714\ufe0f Dos:\n- Ensure the user is aware of the delivery status before proceeding.\n\n\ud83d\udeab Don'ts:\n- Extend the conversation beyond delivery issues.\n\n# Examples\n- **User** : My delivery is late.\n - **Agent response**: I can help with that. Let me fetch your delivery status first.\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : Where is my order? It's delayed.\n - **Agent response**: Let's check your delivery status and resolve this issue.\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : My order hasn't arrived yet.\n - **Agent response**: I apologize for the delay. I'll fetch your delivery status to sort this out.\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : Can you help me with a delayed delivery?\n - **Agent response**: Certainly, I'll look into your delivery status right away.\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : There's an issue with my delivery timing.\n - **Agent response**: Let's verify your delivery status to address this issue.\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)",
|
||||
"model": "gpt-4o",
|
||||
"hasRagSources": false,
|
||||
"controlType": "retain",
|
||||
"tools": [
|
||||
"get_delivery_status"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
"content": "Sender agent: Blog Writer Hub\nContent: Here is your complete blog post on \"Bitcoin\":\n\n---\n\n**Bitcoin: Revolutionizing the World of Digital Currency**\n\nIn recent years, Bitcoin has become a buzzword in financial circles and beyond. As the first and most prominent cryptocurrency, it has sparked debates, inspired innovation, and challenged traditional notions of money. But what exactly is Bitcoin, and why does it continue to capture the world’s attention? Let’s explore the key aspects that define Bitcoin and its growing influence.\n\nAt its core, Bitcoin is a decentralized digital currency that operates without the oversight of a central authority or government. This means that no single entity controls its issuance or transactions, setting it apart from traditional currencies managed by central banks. The decentralized nature of Bitcoin is one of its most revolutionary features, empowering individuals to have greater control over their finances.\n\nThe backbone of Bitcoin’s security and transparency is blockchain technology. Every transaction made with Bitcoin is recorded on a public ledger known as the blockchain. This technology ensures that transactions are secure, tamper-proof, and visible to anyone, fostering trust among users and reducing the risk of fraud.\n\nBitcoin was introduced in 2009 by an anonymous figure or group using the pseudonym Satoshi Nakamoto. The goal was to create an alternative to traditional financial systems, offering a peer-to-peer method of transferring value without intermediaries. This vision has resonated with millions, especially those seeking more autonomy over their money.\n\nOne of the most talked-about aspects of Bitcoin is its price volatility. The value of Bitcoin can fluctuate dramatically, influenced by factors such as market demand, regulatory changes, and broader economic trends. While this volatility presents opportunities for investors, it also introduces risks that potential users should carefully consider.\n\nBitcoin’s uses have evolved over time. Initially, it was seen primarily as a digital alternative to cash. Today, it is widely used for investment purposes, remittances, and transferring value across borders. Its ability to facilitate fast, low-cost international transactions has made it especially popular in regions with limited access to traditional banking services.\n\nHowever, Bitcoin is not without its critics. It has faced scrutiny for its association with illicit activities, given the pseudonymous nature of transactions. Additionally, the process of mining new bitcoins is energy-intensive, raising concerns about its environmental impact.\n\nDespite these challenges, Bitcoin’s journey toward mainstream acceptance continues. More businesses are beginning to accept Bitcoin as payment, and institutional investors are increasingly adding it to their portfolios. This growing adoption signals a shift in how society views and uses money.\n\nIn conclusion, Bitcoin represents a significant shift in the world of finance. Its decentralized structure, innovative technology, and expanding use cases make it a fascinating subject for anyone interested in the future of money. As Bitcoin continues to evolve, it will undoubtedly play a pivotal role in shaping the digital economy of tomorrow.\n\n---\n\nIf you need any edits or want to focus on a specific aspect of Bitcoin, let me know!",
|
||||
"role": "assistant",
|
||||
"sender": "Blog Writer Hub",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"name": "get_order_details",
|
||||
"description": "Tool to fetch details about the user's order.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order_id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the order."
|
||||
}
|
||||
]
|
||||
},
|
||||
"messages": [
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:07.311Z",
|
||||
"role": "assistant",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "62a9b5ec-3dbf-4fdd-85cb-10da37611726",
|
||||
"function": {
|
||||
"name": "transfer_to_agent",
|
||||
"arguments": "{\"assistant\": \"Research Agent\"}"
|
||||
},
|
||||
"required": [
|
||||
"order_id"
|
||||
]
|
||||
},
|
||||
"mockTool": true,
|
||||
"mockInstructions": "Return a mock response for Door Dash order details."
|
||||
},
|
||||
{
|
||||
"name": "get_delivery_status",
|
||||
"description": "Tool to fetch the current status of the delivery.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order_id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the order."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"order_id"
|
||||
]
|
||||
"type": "function"
|
||||
}
|
||||
}
|
||||
],
|
||||
"prompts": [
|
||||
{
|
||||
"name": "Style prompt",
|
||||
"type": "style_prompt",
|
||||
"prompt": "You should be empathetic and helpful."
|
||||
}
|
||||
],
|
||||
"startAgent": "Door Dash Hub"
|
||||
}
|
||||
|
||||
],
|
||||
"agenticSender": "Blog Writer Hub",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:07.311Z",
|
||||
"role": "tool",
|
||||
"content": "{\"assistant\": \"Research Agent\"}",
|
||||
"tool_call_id": "62a9b5ec-3dbf-4fdd-85cb-10da37611726",
|
||||
"tool_name": "transfer_to_agent"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:10.018Z",
|
||||
"role": "assistant",
|
||||
"content": "Bitcoin is a decentralized digital currency that operates without a central authority or government, relying on blockchain technology to record transactions securely and transparently. It was created in 2009 by an anonymous person or group known as Satoshi Nakamoto, aiming to provide an alternative to traditional financial systems. Bitcoin's value is highly volatile, influenced by factors such as market demand, regulatory developments, and macroeconomic trends. It is widely used for investment, remittances, and as a means of transferring value across borders, but it also faces criticism for its association with illicit activities and environmental concerns due to energy-intensive mining processes. Despite these challenges, Bitcoin continues to gain mainstream acceptance, with increasing adoption by businesses and institutional investors.",
|
||||
"agenticSender": "Research Agent",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:10.919Z",
|
||||
"role": "assistant",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "2085cbb1-270e-4c20-a6ef-6f9e4a3d27cf",
|
||||
"function": {
|
||||
"name": "transfer_to_agent",
|
||||
"arguments": "{\"assistant\": \"Bullet Point Agent\"}"
|
||||
},
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"agenticSender": "Blog Writer Hub",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:10.920Z",
|
||||
"role": "tool",
|
||||
"content": "{\"assistant\": \"Bullet Point Agent\"}",
|
||||
"tool_call_id": "2085cbb1-270e-4c20-a6ef-6f9e4a3d27cf",
|
||||
"tool_name": "transfer_to_agent"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:14.776Z",
|
||||
"role": "assistant",
|
||||
"content": "- Bitcoin is a decentralized digital currency that operates without a central authority or government.\n- It uses blockchain technology to securely and transparently record transactions.\n- Created in 2009 by the anonymous Satoshi Nakamoto as an alternative to traditional financial systems.\n- Bitcoin's value is highly volatile, affected by market demand, regulations, and macroeconomic trends.\n- Common uses include investment, remittances, and cross-border value transfers.\n- Criticized for links to illicit activities and environmental impact from energy-intensive mining.\n- Despite challenges, Bitcoin is gaining mainstream acceptance and adoption by businesses and institutional investors.",
|
||||
"agenticSender": "Bullet Point Agent",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:15.946Z",
|
||||
"role": "assistant",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "d48c608a-a660-451b-a3ee-eeebb1cf60ca",
|
||||
"function": {
|
||||
"name": "transfer_to_agent",
|
||||
"arguments": "{\"assistant\": \"Blog Writing Agent\"}"
|
||||
},
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"agenticSender": "Blog Writer Hub",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:15.946Z",
|
||||
"role": "tool",
|
||||
"content": "{\"assistant\": \"Blog Writing Agent\"}",
|
||||
"tool_call_id": "d48c608a-a660-451b-a3ee-eeebb1cf60ca",
|
||||
"tool_name": "transfer_to_agent"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:32.934Z",
|
||||
"role": "assistant",
|
||||
"content": "Bitcoin: Revolutionizing the World of Digital Currency\n\nIn recent years, Bitcoin has become a buzzword in financial circles and beyond. As the first and most prominent cryptocurrency, it has sparked debates, inspired innovation, and challenged traditional notions of money. But what exactly is Bitcoin, and why does it continue to capture the world’s attention? Let’s explore the key aspects that define Bitcoin and its growing influence.\n\nAt its core, Bitcoin is a decentralized digital currency that operates without the oversight of a central authority or government. This means that no single entity controls its issuance or transactions, setting it apart from traditional currencies managed by central banks. The decentralized nature of Bitcoin is one of its most revolutionary features, empowering individuals to have greater control over their finances.\n\nThe backbone of Bitcoin’s security and transparency is blockchain technology. Every transaction made with Bitcoin is recorded on a public ledger known as the blockchain. This technology ensures that transactions are secure, tamper-proof, and visible to anyone, fostering trust among users and reducing the risk of fraud.\n\nBitcoin was introduced in 2009 by an anonymous figure or group using the pseudonym Satoshi Nakamoto. The goal was to create an alternative to traditional financial systems, offering a peer-to-peer method of transferring value without intermediaries. This vision has resonated with millions, especially those seeking more autonomy over their money.\n\nOne of the most talked-about aspects of Bitcoin is its price volatility. The value of Bitcoin can fluctuate dramatically, influenced by factors such as market demand, regulatory changes, and broader economic trends. While this volatility presents opportunities for investors, it also introduces risks that potential users should carefully consider.\n\nBitcoin’s uses have evolved over time. Initially, it was seen primarily as a digital alternative to cash. Today, it is widely used for investment purposes, remittances, and transferring value across borders. Its ability to facilitate fast, low-cost international transactions has made it especially popular in regions with limited access to traditional banking services.\n\nHowever, Bitcoin is not without its critics. It has faced scrutiny for its association with illicit activities, given the pseudonymous nature of transactions. Additionally, the process of mining new bitcoins is energy-intensive, raising concerns about its environmental impact.\n\nDespite these challenges, Bitcoin’s journey toward mainstream acceptance continues. More businesses are beginning to accept Bitcoin as payment, and institutional investors are increasingly adding it to their portfolios. This growing adoption signals a shift in how society views and uses money.\n\nIn conclusion, Bitcoin represents a significant shift in the world of finance. Its decentralized structure, innovative technology, and expanding use cases make it a fascinating subject for anyone interested in the future of money. As Bitcoin continues to evolve, it will undoubtedly play a pivotal role in shaping the digital economy of tomorrow.",
|
||||
"agenticSender": "Blog Writing Agent",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T10:21:38.064Z",
|
||||
"role": "assistant",
|
||||
"content": "Here is your complete blog post on \"Bitcoin\":\n\n---\n\n**Bitcoin: Revolutionizing the World of Digital Currency**\n\nIn recent years, Bitcoin has become a buzzword in financial circles and beyond. As the first and most prominent cryptocurrency, it has sparked debates, inspired innovation, and challenged traditional notions of money. But what exactly is Bitcoin, and why does it continue to capture the world’s attention? Let’s explore the key aspects that define Bitcoin and its growing influence.\n\nAt its core, Bitcoin is a decentralized digital currency that operates without the oversight of a central authority or government. This means that no single entity controls its issuance or transactions, setting it apart from traditional currencies managed by central banks. The decentralized nature of Bitcoin is one of its most revolutionary features, empowering individuals to have greater control over their finances.\n\nThe backbone of Bitcoin’s security and transparency is blockchain technology. Every transaction made with Bitcoin is recorded on a public ledger known as the blockchain. This technology ensures that transactions are secure, tamper-proof, and visible to anyone, fostering trust among users and reducing the risk of fraud.\n\nBitcoin was introduced in 2009 by an anonymous figure or group using the pseudonym Satoshi Nakamoto. The goal was to create an alternative to traditional financial systems, offering a peer-to-peer method of transferring value without intermediaries. This vision has resonated with millions, especially those seeking more autonomy over their money.\n\nOne of the most talked-about aspects of Bitcoin is its price volatility. The value of Bitcoin can fluctuate dramatically, influenced by factors such as market demand, regulatory changes, and broader economic trends. While this volatility presents opportunities for investors, it also introduces risks that potential users should carefully consider.\n\nBitcoin’s uses have evolved over time. Initially, it was seen primarily as a digital alternative to cash. Today, it is widely used for investment purposes, remittances, and transferring value across borders. Its ability to facilitate fast, low-cost international transactions has made it especially popular in regions with limited access to traditional banking services.\n\nHowever, Bitcoin is not without its critics. It has faced scrutiny for its association with illicit activities, given the pseudonymous nature of transactions. Additionally, the process of mining new bitcoins is energy-intensive, raising concerns about its environmental impact.\n\nDespite these challenges, Bitcoin’s journey toward mainstream acceptance continues. More businesses are beginning to accept Bitcoin as payment, and institutional investors are increasingly adding it to their portfolios. This growing adoption signals a shift in how society views and uses money.\n\nIn conclusion, Bitcoin represents a significant shift in the world of finance. Its decentralized structure, innovative technology, and expanding use cases make it a fascinating subject for anyone interested in the future of money. As Bitcoin continues to evolve, it will undoubtedly play a pivotal role in shaping the digital economy of tomorrow.\n\n---\n\nIf you need any edits or want to focus on a specific aspect of Bitcoin, let me know!",
|
||||
"agenticSender": "Blog Writer Hub",
|
||||
"agenticResponseType": "external"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,223 +1,167 @@
|
|||
{
|
||||
"lastRequest": {
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": ""
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "hi"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"sender": "Door Dash Hub",
|
||||
"content": "Hello! Are you facing issues with your order items or delivery timing? How can I assist you today?",
|
||||
"created_at": "2025-03-24T17:33:27.564940"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "my order is missing fries"
|
||||
},
|
||||
{
|
||||
"content": "Agent changed to Door Dash Hub",
|
||||
"role": "assistant",
|
||||
"sender": "Door Dash Hub",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"response_type": "internal"
|
||||
},
|
||||
{
|
||||
"content": "Agent changed to Order Issue",
|
||||
"role": "assistant",
|
||||
"sender": "Order Issue",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"response_type": "internal"
|
||||
},
|
||||
{
|
||||
"content": "Let's resolve this issue by checking your order details. Could you please provide the order ID?",
|
||||
"role": "assistant",
|
||||
"sender": "Order Issue",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "123412"
|
||||
},
|
||||
{
|
||||
"content": "Agent changed to Order Issue",
|
||||
"role": "assistant",
|
||||
"sender": "Order Issue",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"response_type": "internal"
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"role": "assistant",
|
||||
"sender": "Order Issue",
|
||||
"tool_calls": [
|
||||
{
|
||||
"function": {
|
||||
"name": "get_order_details",
|
||||
"arguments": "{\"order_id\":\"123412\"}"
|
||||
},
|
||||
"id": "fc_67e37c622f208192aceec557fbd3125609b4eca638eb3571",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "internal"
|
||||
},
|
||||
{
|
||||
"content": "Order Details for Order ID 123412:\n\n- **Restaurant**: Luigi's Pizzeria \n- **Items Ordered**: \n - 2x Margherita Pizza \n - 1x Garlic Bread \n - 1x Caesar Salad \n- **Estimated Delivery Time**: 7:45 PM \n- **Delivery Address**: 123 Elm Street, Apt 4B \n- **Order Status**: Out for delivery \n- **Total Amount**: $32.75 \n- **Payment Method**: Credit Card (**** **** **** 5678) \n- **Contact**: (123) 456-7890 \n- **Special Instructions**: Leave at the door.",
|
||||
"role": "tool",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": "call_dqCMC5oreOoS9znDDJ7PqWha",
|
||||
"tool_name": null,
|
||||
"response_type": "internal"
|
||||
},
|
||||
{
|
||||
"content": "I checked your order details, and it seems the fries were not included. Could you confirm if there was an oversight in placing the order, or were they supposed to be included?",
|
||||
"role": "assistant",
|
||||
"sender": "Order Issue",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Fries were supposed to be in"
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"last_agent_name": "Order Issue",
|
||||
"tokens": {
|
||||
"total": 1699,
|
||||
"prompt": 1643,
|
||||
"completion": 56
|
||||
}
|
||||
},
|
||||
"agents": [
|
||||
{
|
||||
"name": "Door Dash Hub",
|
||||
"type": "conversation",
|
||||
"description": "Hub agent to manage Door Dash-related queries.",
|
||||
"instructions": "## \ud83e\uddd1\u200d\ud83d\udcbc Role:\nYou are responsible for directing Door Dash-related queries to appropriate agents.\n\n---\n## \u2699\ufe0f Steps to Follow:\n1. Greet the user and ask which Door Dash-related query they need help with (e.g., 'Are you facing issues with your order items or delivery timing?').\n2. If the query matches a specific task, direct the user to the corresponding agent:\n - Order Issue \u2192 [@agent:Order Issue]\n - Delayed Delivery \u2192 [@agent:Delayed Delivery]\n3. If the query doesn't match any specific task, respond with 'I'm sorry, I didn't understand. Could you clarify your request?' or escalate to human support.\n\n---\n## \ud83c\udfaf Scope:\n\u2705 In Scope:\n- Issues with order items\n- Delayed delivery issues\n\n\u274c Out of Scope:\n- Issues unrelated to Door Dash\n- General knowledge queries\n\n---\n## \ud83d\udccb Guidelines:\n\u2714\ufe0f Dos:\n- Direct queries to specific Door Dash agents promptly.\n- Call [@agent:Escalation] agent for unrecognized queries.\n\n\ud83d\udeab Don'ts:\n- Engage in detailed support.\n- Extend the conversation beyond Door Dash.\n- Provide user-facing text such as 'I will connect you now...' when calling another agent\n\n# Examples\n- **User** : I need help with my order items.\n - **Agent actions**: [@agent:Order Issue](#mention)\n\n- **User** : My delivery is delayed.\n - **Agent actions**: Call [@agent:Delayed Delivery](#mention)\n\n- **User** : I'm not sure where my order is.\n - **Agent actions**: Call [@agent:Delayed Delivery](#mention)\n\n- **User** : Can you reset my order settings?\n - **Agent actions**: [@agent:Escalation](#mention)\n\n- **User** : How are you today?\n - **Agent response**: I'm doing great. What would like help with today?",
|
||||
"model": "gpt-4o-mini",
|
||||
"hasRagSources": false,
|
||||
"controlType": "retain",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": [
|
||||
"Order Issue",
|
||||
"Delayed Delivery",
|
||||
"Escalation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Post process",
|
||||
"type": "post_process",
|
||||
"description": "",
|
||||
"instructions": "Ensure that the agent response is terse and to the point.",
|
||||
"model": "gpt-4o-mini",
|
||||
"hasRagSources": false,
|
||||
"controlType": "retain",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Escalation",
|
||||
"type": "escalation",
|
||||
"description": "",
|
||||
"instructions": "Get the user's contact information and let them know that their request has been escalated.",
|
||||
"model": "gpt-4o-mini",
|
||||
"hasRagSources": false,
|
||||
"controlType": "retain",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Order Issue",
|
||||
"type": "conversation",
|
||||
"description": "Agent to assist users with missing or incorrect order items.",
|
||||
"instructions": "## \ud83e\uddd1\u200d\ud83d\udcbc Role:\nAssist users with issues related to missing or incorrect order items.\n\n---\n## \u2699\ufe0f Steps to Follow:\n1. Fetch the order details using the [@tool:get_order_details] tool.\n2. Confirm the issue with the user.\n3. Provide solutions or escalate if unresolved.\n\n---\n## \ud83c\udfaf Scope:\n\u2705 In Scope:\n- Handling missing or incorrect order items\n\n\u274c Out of Scope:\n- Delayed delivery issues\n- General knowledge queries\n\n---\n## \ud83d\udccb Guidelines:\n\u2714\ufe0f Dos:\n- Ensure the user is aware of the order details before proceeding.\n\n\ud83d\udeab Don'ts:\n- Extend the conversation beyond order issues.\n\n# Examples\n- **User** : I received the wrong item in my order.\n - **Agent response**: I can help with that. Let me fetch your order details first.\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : My order is missing an item.\n - **Agent response**: Let's check your order details and resolve this issue.\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : I got someone else's order.\n - **Agent response**: I apologize for the mix-up. I'll fetch your order details to sort this out.\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : Can you help me with a missing item?\n - **Agent response**: Certainly, I'll look into your order details right away.\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : There's an issue with my order items.\n - **Agent response**: Let's verify your order details to address this issue.\n - **Agent actions**: Call [@tool:get_order_details](#mention)",
|
||||
"model": "gpt-4o",
|
||||
"hasRagSources": false,
|
||||
"controlType": "retain",
|
||||
"tools": [
|
||||
"get_order_details"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Delayed Delivery",
|
||||
"type": "conversation",
|
||||
"description": "Agent to assist users with delayed delivery issues.",
|
||||
"instructions": "## \ud83e\uddd1\u200d\ud83d\udcbc Role:\nAssist users with issues related to delayed delivery.\n\n---\n## \u2699\ufe0f Steps to Follow:\n1. Fetch the delivery status using the [@tool:get_delivery_status] tool.\n2. Confirm the delay with the user.\n3. Provide solutions or escalate if unresolved.\n\n---\n## \ud83c\udfaf Scope:\n\u2705 In Scope:\n- Handling delayed delivery issues\n\n\u274c Out of Scope:\n- Missing or incorrect order items\n- General knowledge queries\n\n---\n## \ud83d\udccb Guidelines:\n\u2714\ufe0f Dos:\n- Ensure the user is aware of the delivery status before proceeding.\n\n\ud83d\udeab Don'ts:\n- Extend the conversation beyond delivery issues.\n\n# Examples\n- **User** : My delivery is late.\n - **Agent response**: I can help with that. Let me fetch your delivery status first.\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : Where is my order? It's delayed.\n - **Agent response**: Let's check your delivery status and resolve this issue.\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : My order hasn't arrived yet.\n - **Agent response**: I apologize for the delay. I'll fetch your delivery status to sort this out.\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : Can you help me with a delayed delivery?\n - **Agent response**: Certainly, I'll look into your delivery status right away.\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : There's an issue with my delivery timing.\n - **Agent response**: Let's verify your delivery status to address this issue.\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)",
|
||||
"model": "gpt-4o",
|
||||
"hasRagSources": false,
|
||||
"controlType": "retain",
|
||||
"tools": [
|
||||
"get_delivery_status"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"name": "get_order_details",
|
||||
"description": "Tool to fetch details about the user's order.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order_id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the order."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"order_id"
|
||||
]
|
||||
},
|
||||
"mockTool": true,
|
||||
"mockInstructions": "Return a mock response for Door Dash order details."
|
||||
},
|
||||
{
|
||||
"name": "get_delivery_status",
|
||||
"description": "Tool to fetch the current status of the delivery.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order_id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the order."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"order_id"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"prompts": [
|
||||
{
|
||||
"name": "Style prompt",
|
||||
"type": "style_prompt",
|
||||
"prompt": "You should be empathetic and helpful."
|
||||
}
|
||||
],
|
||||
"startAgent": "Door Dash Hub"
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": ""
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-05T17:29:15.822Z",
|
||||
"role": "assistant",
|
||||
"content": "How can I help you today?",
|
||||
"agenticSender": "Interview Evaluation Hub",
|
||||
"agenticResponseType": "external"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "[2025-05-02, 10:00] Assistant: Good morning. Thank you for taking the time to speak with us today. Could you start by briefly walking us through your leadership journey over the past decade?\n\n[2025-05-02, 10:01] User: Absolutely. Over the past 12 years, I've led product and technology teams across high-growth B2B SaaS companies. Most recently, I was the Chief Product Officer at a fintech scaleup, where I built a team of 60+ PMs and oversaw the expansion into three international markets. Prior to that, I was VP of Product at a healthtech startup acquired by a public company.\n\n[2025-05-02, 10:03] Assistant: Impressive. What would you say is your core leadership style, and how has it evolved over the years?\n\n[2025-05-02, 10:04] User: I'd describe my style as mission-oriented and data-driven. Early in my career, I leaned heavily on execution. But with time, I've grown more focused on setting context and empowering teams. At my last company, I implemented OKRs org-wide and focused on building a culture of trust and transparency.\n\n[2025-05-02, 10:06] Assistant: How do you approach scaling an org while preserving agility and innovation?\n\n[2025-05-02, 10:07] User: Great question. I believe the key is in modular team design. At the fintech company, we adopted a \"startup within a startup\" model—each pod owned metrics and had embedded PMs, designers, and engineers. This preserved autonomy and speed while scaling. We also invested heavily in internal tooling and rituals like demo days to keep the culture of innovation alive.\n\n[2025-05-02, 10:10] Assistant: Tell me about a time you made a high-stakes decision without perfect data.\n\n[2025-05-02, 10:11] User: In early 2023, we had to decide whether to sunset a low-performing but vocal customer-facing feature. We lacked conclusive cohort data but saw consistent qualitative feedback about confusion and churn. I convened a task force, aligned with customer success and execs, and made the call. Within 60 days, churn dropped by 12%, validating the bet.\n\n[2025-05-02, 10:14] Assistant: Final question—what excites you about potentially joining our company as a Chief Product Officer?\n\n[2025-05-02, 10:15] User: Your mission aligns with what I care about—bringing transparency and efficiency to financial infrastructure. The team's reputation, your engineering culture, and the velocity of product innovation are all huge draws. I believe I can help you scale without losing the edge that got you here.",
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-05T17:29:44.695Z"
|
||||
}
|
||||
],
|
||||
"lastRequest": {
|
||||
"projectId": "a7a91831-e410-4fc5-a31d-8d027e14540a",
|
||||
"messages": [
|
||||
{
|
||||
"content": "",
|
||||
"role": "system",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
},
|
||||
{
|
||||
"content": "How can I help you today?",
|
||||
"role": "assistant",
|
||||
"sender": "Interview Evaluation Hub",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
},
|
||||
{
|
||||
"content": "[2025-05-02, 10:00] Assistant: Good morning. Thank you for taking the time to speak with us today. Could you start by briefly walking us through your leadership journey over the past decade?\n\n[2025-05-02, 10:01] User: Absolutely. Over the past 12 years, I've led product and technology teams across high-growth B2B SaaS companies. Most recently, I was the Chief Product Officer at a fintech scaleup, where I built a team of 60+ PMs and oversaw the expansion into three international markets. Prior to that, I was VP of Product at a healthtech startup acquired by a public company.\n\n[2025-05-02, 10:03] Assistant: Impressive. What would you say is your core leadership style, and how has it evolved over the years?\n\n[2025-05-02, 10:04] User: I'd describe my style as mission-oriented and data-driven. Early in my career, I leaned heavily on execution. But with time, I've grown more focused on setting context and empowering teams. At my last company, I implemented OKRs org-wide and focused on building a culture of trust and transparency.\n\n[2025-05-02, 10:06] Assistant: How do you approach scaling an org while preserving agility and innovation?\n\n[2025-05-02, 10:07] User: Great question. I believe the key is in modular team design. At the fintech company, we adopted a \"startup within a startup\" model—each pod owned metrics and had embedded PMs, designers, and engineers. This preserved autonomy and speed while scaling. We also invested heavily in internal tooling and rituals like demo days to keep the culture of innovation alive.\n\n[2025-05-02, 10:10] Assistant: Tell me about a time you made a high-stakes decision without perfect data.\n\n[2025-05-02, 10:11] User: In early 2023, we had to decide whether to sunset a low-performing but vocal customer-facing feature. We lacked conclusive cohort data but saw consistent qualitative feedback about confusion and churn. I convened a task force, aligned with customer success and execs, and made the call. Within 60 days, churn dropped by 12%, validating the bet.\n\n[2025-05-02, 10:14] Assistant: Final question—what excites you about potentially joining our company as a Chief Product Officer?\n\n[2025-05-02, 10:15] User: Your mission aligns with what I care about—bringing transparency and efficiency to financial infrastructure. The team's reputation, your engineering culture, and the velocity of product innovation are all huge draws. I believe I can help you scale without losing the edge that got you here.",
|
||||
"role": "user",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"last_agent_name": "Interview Evaluation Hub",
|
||||
"tokens": {
|
||||
"total": 0,
|
||||
"prompt": 0,
|
||||
"completion": 0
|
||||
},
|
||||
"turn_messages": [
|
||||
{
|
||||
"content": "How can I help you today?",
|
||||
"role": "assistant",
|
||||
"sender": "Interview Evaluation Hub",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
}
|
||||
]
|
||||
},
|
||||
"agents": [
|
||||
{
|
||||
"name": "Interview Evaluation Hub",
|
||||
"type": "conversation",
|
||||
"description": "Hub agent to orchestrate the evaluation of interview transcripts between an executive search agency and a CxO candidate.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are the hub agent responsible for orchestrating the evaluation of interview transcripts between an executive search agency (Assistant) and a CxO candidate (User).\n\n---\n## ⚙️ Steps to Follow:\n1. Receive the transcript in the specified format.\n2. FIRST: Send the transcript to [@agent:Evaluation Agent] for evaluation.\n3. Wait to receive the complete evaluation from the Evaluation Agent.\n4. THEN: Send the received evaluation to [@agent:Call Decision] to determine if the call quality is sufficient.\n5. Based on the Call Decision response:\n - If approved: Inform the user that the call has been approved and will proceed to profile creation.\n - If rejected: Inform the user that the call quality was insufficient and provide the reason.\n6. Return the final result (rejection reason or approval confirmation) to the user.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Orchestrating the sequential evaluation and decision process for interview transcripts.\n\n❌ Out of Scope:\n- Directly evaluating or creating profiles.\n- Handling transcripts not in the specified format.\n- Interacting with the individual evaluation agents.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Follow the strict sequence: Evaluation Agent first, then Call Decision.\n- Wait for each agent's complete response before proceeding.\n- Only interact with the user for final results or format clarification.\n\n🚫 Don'ts:\n- Do not perform evaluation or profile creation yourself.\n- Do not modify the transcript.\n- Do not try to get evaluations simultaneously.\n- Do not reference the individual evaluation agents.\n- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent (a handoff). You must only put out 1 transfer related tool call in one output.\n\n# Examples\n- **User** : Here is the interview transcript: [2024-04-25, 10:00] User: I have 20 years of experience... [2024-04-25, 10:01] Assistant: Can you describe your leadership style?\n - **Agent actions**: \n 1. First call [@agent:Evaluation Agent](#mention)\n 2. Wait for complete evaluation\n 3. Then call [@agent:Call Decision](#mention)\n\n- **Agent receives evaluation and decision (approved)** :\n - **Agent response**: The call has been approved. Proceeding to candidate profile creation.\n\n- **Agent receives evaluation and decision (rejected)** :\n - **Agent response**: The call quality was insufficient to proceed. [Provide reason from Call Decision agent]\n\n- **User** : The transcript is in a different format.\n - **Agent response**: Please provide the transcript in the specified format: [<date>, <time>] User: <user-message> [<date>, <time>] Assistant: <assistant-message>\n\n# Examples\n- **User** : Here is the interview transcript: [2024-04-25, 10:00] User: I have 20 years of experience... [2024-04-25, 10:01] Assistant: Can you describe your leadership style?\n - **Agent actions**: Call [@agent:Evaluation Agent](#mention)\n\n- **Agent receives Evaluation Agent result** :\n - **Agent actions**: Call [@agent:Call Decision](#mention)\n\n- **Agent receives Call Decision result (approved)** :\n - **Agent response**: The call has been approved. Proceeding to candidate profile creation.\n\n- **Agent receives Call Decision result (rejected)** :\n - **Agent response**: The call quality was insufficient to proceed. [Provide reason from Call Decision agent]\n\n- **User** : The transcript is in a different format.\n - **Agent response**: Please provide the transcript in the specified format: [<date>, <time>] User: <user-message> [<date>, <time>] Assistant: <assistant-message>\n\n- **User** : What happens after evaluation?\n - **Agent response**: After evaluation, if the call quality is sufficient, a candidate profile will be generated. Otherwise, you will receive feedback on why the call was rejected.",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "user_facing",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": [
|
||||
"Evaluation Agent",
|
||||
"Call Decision"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Exec Search Evaluation",
|
||||
"type": "conversation",
|
||||
"description": "Evaluates the relevance and quality of questions asked by the executive search agency in the transcript.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for evaluating the questions asked by the executive search agency (Assistant) in the interview transcript.\n\n---\n## ⚙️ Steps to Follow:\n1. Review all 'Assistant' messages in the transcript.\n2. Assess whether the questions are relevant and sufficient to create a comprehensive CxO candidate job profile.\n3. Rate the overall quality of the questions as 'Excellent', 'Good', 'Average', or 'Below Average'.\n4. Provide a brief justification for your rating.\n5. Return your evaluation and justification.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Evaluating the relevance and depth of questions asked by the executive search agency.\n\n❌ Out of Scope:\n- Evaluating candidate responses.\n- Creating candidate profiles.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Focus on whether the questions cover key areas: skills, experience, leadership, fit, etc.\n- Be objective and concise in your justification.\n\n🚫 Don'ts:\n- Do not evaluate candidate answers.\n- Do not suggest improvements unless specifically asked.\n\n# Examples\n- **User** : [2024-04-25, 10:01] Assistant: Can you describe your leadership style? [2024-04-25, 10:02] Assistant: What are your key achievements as a CTO?\n - **Agent response**: Rating: Good. The questions are relevant and cover important aspects of the candidate's experience and leadership.\n\n- **User** : [2024-04-25, 10:01] Assistant: Tell me about yourself. [2024-04-25, 10:02] Assistant: What do you like to do in your free time?\n - **Agent response**: Rating: Below Average. The questions are too generic and do not sufficiently address the candidate's professional qualifications.\n\n- **User** : [2024-04-25, 10:01] Assistant: What is your favorite color?\n - **Agent response**: Rating: Below Average. The question is irrelevant for creating a CxO candidate profile.\n\n- **User** : [2024-04-25, 10:01] Assistant: Can you walk me through your experience leading large teams?\n - **Agent response**: Rating: Excellent. The question is highly relevant for assessing leadership skills.\n\n- **User** : [2024-04-25, 10:01] Assistant: How do you handle stress at work?\n - **Agent response**: Rating: Good. The question is relevant for understanding the candidate's coping mechanisms in high-pressure environments.",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "internal",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Candidate Evaluation",
|
||||
"type": "conversation",
|
||||
"description": "Evaluates the quality, relevance, and clarity of the candidate's (User) responses in the transcript.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for evaluating the candidate's (User) responses in the interview transcript.\n\n---\n## ⚙️ Steps to Follow:\n1. Review all 'User' messages in the transcript.\n2. Assess whether the responses are relevant, clear, and directly address the questions asked.\n3. Identify if any responses are meandering, off-topic, or spam.\n4. Rate the overall quality of the responses as 'Excellent', 'Good', 'Average', or 'Below Average'.\n5. Provide a brief justification for your rating.\n6. Return your evaluation and justification.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Evaluating the quality and relevance of candidate responses.\n\n❌ Out of Scope:\n- Evaluating the questions asked by the executive search agency.\n- Creating candidate profiles.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Focus on clarity, relevance, and completeness of responses.\n- Be objective and concise in your justification.\n\n🚫 Don'ts:\n- Do not evaluate the questions asked.\n- Do not suggest improvements unless specifically asked.\n\n# Examples\n- **User** : [2024-04-25, 10:01] User: I have led teams of 100+ engineers and delivered multiple large-scale projects on time.\n - **Agent response**: Rating: Excellent. The response is clear, relevant, and directly addresses the question.\n\n- **User** : [2024-04-25, 10:01] User: Well, you know, it's like, sometimes things just happen, and I just do my best, you know?\n - **Agent response**: Rating: Below Average. The response is vague and does not provide meaningful information.\n\n- **User** : [2024-04-25, 10:01] User: I have experience in many things, but let's talk about something else.\n - **Agent response**: Rating: Below Average. The response is off-topic and does not address the question.\n\n- **User** : [2024-04-25, 10:01] User: I have successfully managed cross-functional teams and implemented new processes that improved efficiency by 20%.\n - **Agent response**: Rating: Good. The response is relevant and provides specific achievements.\n\n- **User** : [2024-04-25, 10:01] User: I am a hard worker.\n - **Agent response**: Rating: Average. The response is generic and lacks detail.",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "internal",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Call Decision",
|
||||
"type": "conversation",
|
||||
"description": "Decides whether the call quality is sufficient to proceed to candidate profile creation based on the evaluations.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for making a decision based on the evaluations from the Exec Search and Candidate Evaluation agents.\n\n---\n## ⚙️ Steps to Follow:\n1. Receive the ratings and justifications from both evaluation agents.\n2. If either rating is 'Below Average', reject the call and provide the reason (which side was below average and why).\n3. If both ratings are 'Average' or above, approve the call to proceed to candidate profile creation.\n4. Return your decision and, if rejected, the reason for rejection.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Deciding whether to proceed to profile creation or reject the call.\n\n❌ Out of Scope:\n- Evaluating the transcript directly.\n- Creating candidate profiles.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Be clear and concise in your decision.\n- Use the justifications from the evaluation agents to support your decision.\n\n🚫 Don'ts:\n- Do not override the evaluation agents' ratings.\n\n# Examples\n- **User** : Exec Search: Good. Candidate: Excellent.\n - **Agent response**: Decision: Approved. Both sides performed well. Proceed to candidate profile creation.\n\n- **User** : Exec Search: Below Average. Candidate: Good.\n - **Agent response**: Decision: Rejected. The executive search agency's questions were below average: [reason].\n\n- **User** : Exec Search: Good. Candidate: Below Average.\n - **Agent response**: Decision: Rejected. The candidate's responses were below average: [reason].\n\n- **User** : Exec Search: Average. Candidate: Average.\n - **Agent response**: Decision: Approved. Both sides performed adequately. Proceed to candidate profile creation.\n\n- **User** : Exec Search: Excellent. Candidate: Excellent.\n - **Agent response**: Decision: Approved. Both sides performed excellently. Proceed to candidate profile creation.",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "internal",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Evaluation Agent",
|
||||
"type": "conversation",
|
||||
"description": "Coordinates the evaluation of the interview transcript by both the Exec Search Evaluation and Candidate Evaluation agents.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for coordinating the evaluation of the interview transcript by both the Exec Search Evaluation and Candidate Evaluation agents.\n\n---\n## ⚙️ Steps to Follow:\n1. Receive the transcript from the hub agent.\n2. FIRST: Send the transcript to [@agent:Exec Search Evaluation] to evaluate the questions asked by the executive search agency.\n3. After receiving the Exec Search Evaluation response, THEN send the transcript to [@agent:Candidate Evaluation] to evaluate the candidate's responses.\n4. Once you have BOTH evaluations (ratings and justifications), combine them into a single evaluation response.\n5. Return the combined evaluation to the hub agent.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Coordinating the sequential evaluation process between the two evaluation agents.\n\n❌ Out of Scope:\n- Making decisions about call quality.\n- Creating candidate profiles.\n- Interacting directly with the user.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Follow the strict sequence: Exec Search first, then Candidate.\n- Wait for each evaluation to complete before proceeding.\n- Combine both evaluations into a single response.\n\n🚫 Don'ts:\n- Do not evaluate the transcript yourself.\n- Do not try to get both evaluations simultaneously.\n- Do not interact with the user.\n- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent (a handoff). You must only put out 1 transfer related tool call in one output.\n\n# Examples\n- **User** : [2024-04-25, 10:01] User: I have 20 years of experience... [2024-04-25, 10:01] Assistant: Can you describe your leadership style?\n - **Agent actions**: \n 1. First call [@agent:Exec Search Evaluation](#mention)\n 2. After receiving response, then call [@agent:Candidate Evaluation](#mention)\n 3. Combine both evaluations into single response\n\n- **Agent receives both evaluations** :\n - **Agent response**: Returns combined evaluations (ratings and justifications) to the hub agent.\n\n# Examples\n- **User** : [2024-04-25, 10:01] User: I have 20 years of experience... [2024-04-25, 10:01] Assistant: Can you describe your leadership style?\n - **Agent actions**: Call [@agent:Exec Search Evaluation](#mention), Call [@agent:Candidate Evaluation](#mention)\n\n- **Agent receives both evaluations** :\n - **Agent response**: Returns both evaluations (ratings and justifications) to the hub agent.",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "internal",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": [
|
||||
"Exec Search Evaluation",
|
||||
"Candidate Evaluation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"name": "web_search",
|
||||
"description": "Fetch information from the web based on chat context",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"isLibrary": true
|
||||
}
|
||||
],
|
||||
"prompts": [],
|
||||
"startAgent": "Interview Evaluation Hub",
|
||||
"mcpServers": [],
|
||||
"toolWebhookUrl": ""
|
||||
},
|
||||
"lastResponse": null
|
||||
}
|
||||
|
|
@ -1,270 +1,385 @@
|
|||
{
|
||||
"lastRequest": {
|
||||
"projectId": "4ebd5e81-010a-4bc6-91e0-0aa98173dbac",
|
||||
"messages": [
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": ""
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T07:17:21.052Z",
|
||||
"role": "assistant",
|
||||
"content": "How can I help you today?",
|
||||
"agenticSender": "Credit Card Hub",
|
||||
"agenticResponseType": "external"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What is the process for applying for a card?",
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T07:17:31.404Z"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T07:17:32.671Z",
|
||||
"role": "assistant",
|
||||
"tool_calls": [
|
||||
{
|
||||
"content": "",
|
||||
"role": "system",
|
||||
"sender": null,
|
||||
"id": "404dafe5-7b5b-4617-bd2f-ecac3119c928",
|
||||
"function": {
|
||||
"name": "transfer_to_agent",
|
||||
"arguments": "{\"assistant\": \"Card Application Process\"}"
|
||||
},
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"agenticSender": "Credit Card Hub",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T07:17:32.671Z",
|
||||
"role": "tool",
|
||||
"content": "{\"assistant\": \"Card Application Process\"}",
|
||||
"tool_call_id": "404dafe5-7b5b-4617-bd2f-ecac3119c928",
|
||||
"tool_name": "transfer_to_agent"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T07:17:33.734Z",
|
||||
"role": "assistant",
|
||||
"content": "Which credit card are you interested in applying for?",
|
||||
"agenticSender": "Card Application Process",
|
||||
"agenticResponseType": "external"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Amex centurion",
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T07:17:41.963Z"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T07:17:55.790Z",
|
||||
"role": "assistant",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "ws_6819b79705a08192a819334a0c7900b6004e105788fca541",
|
||||
"function": {
|
||||
"name": "web_search",
|
||||
"arguments": "{\"search_id\": \"ws_6819b79705a08192a819334a0c7900b6004e105788fca541\"}"
|
||||
},
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"agenticSender": "Card Application Process",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T07:17:55.790Z",
|
||||
"role": "assistant",
|
||||
"content": "The American Express Centurion Card, commonly known as the \"Black Card,\" is an invitation-only charge card designed for high-net-worth individuals. To be considered for an invitation, you typically need to meet the following criteria:\n\n- **Existing American Express Relationship**: You should have an active American Express card account.\n\n- **High Annual Spending**: While American Express does not publicly disclose specific spending thresholds, reports suggest that spending between $250,000 and $500,000 annually on your American Express cards may increase your chances of receiving an invitation. ([nerdwallet.com](https://www.nerdwallet.com/article/travel/centurion-black-card?utm_source=openai))\n\n- **Luxury Spending Patterns**: Expenditures on luxury items, such as high-end travel, fine dining, and premium goods, are considered favorable. ([fool.com](https://www.fool.com/the-ascent/credit-cards/articles/your-guide-to-getting-an-amex-black-card/?utm_source=openai))\n\n- **Excellent Credit Score**: A credit score of 800 or higher is typically expected. ([finance.yahoo.com](https://finance.yahoo.com/personal-finance/american-express-centurion-card-213619679.html?utm_source=openai))\n\nIf you believe you meet these criteria, you can express your interest by visiting the official Centurion website and submitting a request for consideration. Please note that submitting a request does not guarantee an invitation, as American Express evaluates each request based on its internal criteria. ([thepointsguy.com](https://thepointsguy.com/news/you-can-now-request-an-invite-to-the-amex-centurion-card/?utm_source=openai))\n\nShould you receive an invitation and choose to accept it, be prepared for the following fees:\n\n- **Initiation Fee**: A one-time fee of $10,000.\n\n- **Annual Fee**: An ongoing annual fee of $5,000. ([forbes.com](https://www.forbes.com/advisor/credit-cards/reviews/centurion-from-american-express/?utm_source=openai))\n\nPlease be aware that the Centurion Card is designed for individuals with substantial financial resources and specific spending habits. If you do not meet these criteria, American Express offers a range of other premium cards that might better suit your needs. ",
|
||||
"agenticSender": "Card Application Process",
|
||||
"agenticResponseType": "external"
|
||||
}
|
||||
],
|
||||
"lastRequest": {
|
||||
"projectId": "05d64a45-8468-4783-b5df-c9644d2d7ba5",
|
||||
"messages": [
|
||||
{
|
||||
"content": "",
|
||||
"role": "system",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
},
|
||||
{
|
||||
"content": "How can I help you today?",
|
||||
"role": "assistant",
|
||||
"sender": "Credit Card Hub",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
},
|
||||
{
|
||||
"content": "What is the process for applying for a card?",
|
||||
"role": "user",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
},
|
||||
{
|
||||
"content": null,
|
||||
"role": "assistant",
|
||||
"sender": "Credit Card Hub",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "404dafe5-7b5b-4617-bd2f-ecac3119c928",
|
||||
"function": {
|
||||
"name": "transfer_to_agent",
|
||||
"arguments": "{\"assistant\": \"Card Application Process\"}"
|
||||
},
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "internal"
|
||||
},
|
||||
{
|
||||
"content": "{\"assistant\": \"Card Application Process\"}",
|
||||
"role": "tool",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": "404dafe5-7b5b-4617-bd2f-ecac3119c928",
|
||||
"tool_name": "transfer_to_agent"
|
||||
},
|
||||
{
|
||||
"content": "Which credit card are you interested in applying for?",
|
||||
"role": "assistant",
|
||||
"sender": "Card Application Process",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
},
|
||||
{
|
||||
"content": "Amex centurion",
|
||||
"role": "user",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"last_agent_name": "Card Application Process",
|
||||
"tokens": {
|
||||
"total": 0,
|
||||
"prompt": 0,
|
||||
"completion": 0
|
||||
},
|
||||
"turn_messages": [
|
||||
{
|
||||
"content": "Sender agent: Card Application Process\nContent: Which credit card are you interested in applying for?",
|
||||
"role": "assistant",
|
||||
"sender": "Card Application Process",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
}
|
||||
]
|
||||
},
|
||||
"agents": [
|
||||
{
|
||||
"name": "Credit Card Hub",
|
||||
"type": "conversation",
|
||||
"description": "Hub agent to route credit card related queries to the appropriate sub-agent.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are the hub for all credit card related queries. Your job is to understand the user's intent and route them to the correct agent: recommendations, benefits, or application process.\n\n---\n## ⚙️ Steps to Follow:\n1. If the user wants card recommendations, call [@agent:Card Recommendation].\n2. If the user asks about card benefits or features, call [@agent:Card Benefits].\n3. If the user wants to know about the application process, call [@agent:Card Application Process].\n4. If the query is unclear, ask a clarifying question.\n5. If the query is out of scope, politely inform the user.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Routing queries about credit card recommendations, benefits, and application process.\n\n❌ Out of Scope:\n- Non-credit card related queries.\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Be professional and friendly.\n- Ask clarifying questions if unsure.\n\n🚫 Don'ts:\n- Do not answer detailed questions yourself; always route to the correct agent.\n- Do not say 'connecting you to an agent'.\n\n\n# Examples\n- **User**: I want to know which credit card is best for travel.\n - **Agent actions**: Call [@agent:Card Recommendation](#mention)\n\n- **User**: What are the benefits of your platinum card?\n - **Agent actions**: Call [@agent:Card Benefits](#mention)\n\n- **User**: How do I apply for a credit card?\n - **Agent actions**: Call [@agent:Card Application Process](#mention)\n\n- **User**: Can you help me with my savings account?\n - **Agent response**: I'm sorry, I can only assist with credit card related queries.\n\n- **User**: Hi, I have a question.\n - **Agent response**: Hello! How can I help you with credit cards today?",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": [
|
||||
"Card Recommendation",
|
||||
"Card Benefits",
|
||||
"Card Application Process"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Card Recommendation",
|
||||
"type": "conversation",
|
||||
"description": "Recommends suitable credit cards based on user preferences and needs.",
|
||||
"instructions": "## 🧑💼 Role:\nHelp users find the best credit card for their needs by asking about their preferences and providing tailored recommendations.\n\n---\n## ⚙️ Steps to Follow:\n1. Ask the user about their preferences (e.g., travel, cashback, low interest, rewards). If they have already mentioned one of the categories, skip to the next step directly. \n2. Use [@tool:recommendation] to find suitable credit card matching their needs.\n3. If the request is out of scope, call [@agent:Credit Card Hub] \n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Recommending credit cards based on user needs.\n- Explaining why a card is recommended.\n\n❌ Out of Scope:\n- Detailed card benefits (refer to Card Benefits agent).\n- Application process (refer to Card Application Process agent).\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Be professional and friendly.\n- Tailor recommendations to user input.\n\n🚫 Don'ts:\n- Recommend cards without understanding user needs.\n\n\n\n\n# Examples\n- **User**: I want a card with good travel rewards.\n - **Agent actions**: Call [@tool:web_search]\n - **Agent response**: Based on your interest in travel rewards, here are some great options: 1. Travel Platinum Card – excellent for frequent flyers, 2. Explorer Rewards Card – great for international travel. Would you like more details on any of these?\n\n- **User**: Which card is best for cashback?\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: For cashback, I recommend: 1. Cashback Plus Card – up to 5% cashback on groceries, 2. Everyday Saver Card – 2% cashback on all purchases. Would you like to know more?\n\n- **User**: I want a card with a low interest rate.\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: Here are some low interest rate cards: 1. Low Rate Advantage Card, 2. Simple Interest Card. Let me know if you want more information.\n\n- **User**: Can you recommend a card for students?\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: For students, these cards are popular: 1. Student Starter Card, 2. Young Achiever Card. Would you like details on their features?\n\n- **User**: What if I want both travel and cashback?\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: There are hybrid cards that offer both travel rewards and cashback. Here are two options: 1. Flexi Rewards Card, 2. Global Cashback Card. Would you like to compare them?",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "user_facing",
|
||||
"tools": [
|
||||
"recommendation",
|
||||
"web_search"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": [
|
||||
"Credit Card Hub"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Card Benefits",
|
||||
"type": "conversation",
|
||||
"description": "Provides detailed information about the benefits and features of specific credit cards.",
|
||||
"instructions": "## 🧑💼 Role:\nAnswer user questions about the benefits, features, and perks of credit cards.\n\n---\n## ⚙️ Steps to Follow:\n1. Ask the user which card they want to know about if not specified.\n2. Use [@tool:web_search] to find up-to-date information about the card's benefits.\n3. Summarize the key benefits and features in a clear, friendly, and professional manner.\n4. Offer to answer follow-up questions or compare with other cards.\n5. If the request is out of scope, call [@agent:Credit Card Hub].\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Explaining card benefits, features, and perks.\n- Comparing benefits between cards.\n\n❌ Out of Scope:\n- Recommending cards (refer to Card Recommendation agent).\n- Application process (refer to Card Application Process agent).\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Be accurate, clear, and concise.\n- Use up-to-date information.\n\n🚫 Don'ts:\n- Speculate about benefits; always check with web_search.\n\n# Examples\n- **User**: What are the benefits of the Platinum Card?\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: The Platinum Card offers benefits such as airport lounge access, travel insurance, and high reward points on travel spending. Would you like more details on any of these?\n\n- **User**: Does the Cashback Plus Card have purchase protection?\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: Yes, the Cashback Plus Card includes purchase protection for eligible items. Would you like to know the coverage details?\n\n- **User**: Can you compare the benefits of the Student Starter Card and the Young Achiever Card?\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: Here’s a comparison: Student Starter Card – no annual fee, basic rewards; Young Achiever Card – higher rewards, some travel perks. Would you like a detailed breakdown?\n\n- **User**: What perks come with the Explorer Rewards Card?\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: The Explorer Rewards Card offers perks like complimentary travel insurance, bonus points on international purchases, and concierge service.\n\n- **User**: Tell me about the insurance benefits of the Low Rate Advantage Card.\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: The Low Rate Advantage Card includes travel accident insurance and extended warranty on purchases. Would you like more information?",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "user_facing",
|
||||
"tools": [
|
||||
"web_search"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": [
|
||||
"Credit Card Hub"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Card Application Process",
|
||||
"type": "conversation",
|
||||
"description": "Guides users through the credit card application process, including eligibility and required documents.",
|
||||
"instructions": "## 🧑💼 Role:\nGuide users through the process of applying for a credit card, including eligibility, required documents, and next steps.\n\n---\n## ⚙️ Steps to Follow:\n1. Ask the user which card they want to apply for - if they haven't already told you.\n2. Using [@tool:web_search], search the web for the needed information. You have to use the web_search tool without fail. You cannot make up information.\n3. If the request is out of scope, call [@agent:Credit Card Hub].\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Explaining application steps, eligibility, and required documents.\n- Guiding users through the process.\n\n❌ Out of Scope:\n- Recommending cards (refer to Card Recommendation agent).\n- Explaining card benefits (refer to Card Benefits agent).\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Be clear, step-by-step, and supportive.\n- Use up-to-date information.\n\n🚫 Don'ts:\n- Provide application links unless verified via web_search.\n\n\n# Examples\n- **User**: How do I apply for the Platinum Card?\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: To apply for the Platinum Card, you’ll need to meet the eligibility criteria (e.g., minimum income, age) and provide documents like ID and proof of income. You can apply online or at a branch. Would you like a step-by-step guide?\n\n- **User**: What documents do I need to apply for a credit card?\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: Typically, you’ll need a government-issued ID, proof of income, and address verification. Would you like details for a specific card?\n\n- **User**: Can I apply for a card as a student?\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: Many banks offer student credit cards with relaxed eligibility. You’ll usually need proof of enrollment and a student ID. Would you like to see some options?\n\n- **User**: Is there an online application for the Cashback Plus Card?\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: Yes, you can apply online for the Cashback Plus Card. I can guide you through the steps or provide the official link if you’d like.\n\n- **User**: What’s the minimum income for the Explorer Rewards Card?\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: The minimum income requirement for the Explorer Rewards Card is typically $30,000 per year. Would you like more details on eligibility?",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "user_facing",
|
||||
"tools": [
|
||||
"web_search"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": [
|
||||
"Credit Card Hub"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"name": "web_search",
|
||||
"description": "Fetch information from the web based on chat context",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"isLibrary": true
|
||||
},
|
||||
{
|
||||
"name": "recommendation",
|
||||
"description": "Recommends a card",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
"mockTool": true,
|
||||
"mockInstructions": "Recommend a random credit card"
|
||||
}
|
||||
],
|
||||
"prompts": [],
|
||||
"startAgent": "Credit Card Hub",
|
||||
"mcpServers": [],
|
||||
"toolWebhookUrl": ""
|
||||
},
|
||||
"lastResponse": {
|
||||
"state": {
|
||||
"last_agent_name": "Card Application Process",
|
||||
"tokens": {
|
||||
"total": 0,
|
||||
"prompt": 0,
|
||||
"completion": 0
|
||||
},
|
||||
"turn_messages": [
|
||||
{
|
||||
"content": "Hello! How can I assist you with your DoorDash query today? Are you inquiring about a missing item, delivery status, or subscription details?",
|
||||
"content": "Sender agent: Card Application Process\nContent: None",
|
||||
"role": "assistant",
|
||||
"sender": "DoorDash Support Hub",
|
||||
"tool_calls": null,
|
||||
"sender": "Card Application Process",
|
||||
"tool_calls": [
|
||||
{
|
||||
"function": {
|
||||
"name": "web_search",
|
||||
"arguments": "{\"search_id\": \"ws_6819b79705a08192a819334a0c7900b6004e105788fca541\"}"
|
||||
},
|
||||
"id": "ws_6819b79705a08192a819334a0c7900b6004e105788fca541",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "internal"
|
||||
},
|
||||
{
|
||||
"content": "Hello! How can I assist you with your DoorDash query today? Are you inquiring about a missing item, delivery status, or subscription details?",
|
||||
"content": "Sender agent: Card Application Process\nContent: The American Express Centurion Card, commonly known as the \"Black Card,\" is an invitation-only charge card designed for high-net-worth individuals. To be considered for an invitation, you typically need to meet the following criteria:\n\n- **Existing American Express Relationship**: You should have an active American Express card account.\n\n- **High Annual Spending**: While American Express does not publicly disclose specific spending thresholds, reports suggest that spending between $250,000 and $500,000 annually on your American Express cards may increase your chances of receiving an invitation. ([nerdwallet.com](https://www.nerdwallet.com/article/travel/centurion-black-card?utm_source=openai))\n\n- **Luxury Spending Patterns**: Expenditures on luxury items, such as high-end travel, fine dining, and premium goods, are considered favorable. ([fool.com](https://www.fool.com/the-ascent/credit-cards/articles/your-guide-to-getting-an-amex-black-card/?utm_source=openai))\n\n- **Excellent Credit Score**: A credit score of 800 or higher is typically expected. ([finance.yahoo.com](https://finance.yahoo.com/personal-finance/american-express-centurion-card-213619679.html?utm_source=openai))\n\nIf you believe you meet these criteria, you can express your interest by visiting the official Centurion website and submitting a request for consideration. Please note that submitting a request does not guarantee an invitation, as American Express evaluates each request based on its internal criteria. ([thepointsguy.com](https://thepointsguy.com/news/you-can-now-request-an-invite-to-the-amex-centurion-card/?utm_source=openai))\n\nShould you receive an invitation and choose to accept it, be prepared for the following fees:\n\n- **Initiation Fee**: A one-time fee of $10,000.\n\n- **Annual Fee**: An ongoing annual fee of $5,000. ([forbes.com](https://www.forbes.com/advisor/credit-cards/reviews/centurion-from-american-express/?utm_source=openai))\n\nPlease be aware that the Centurion Card is designed for individuals with substantial financial resources and specific spending habits. If you do not meet these criteria, American Express offers a range of other premium cards that might better suit your needs. ",
|
||||
"role": "assistant",
|
||||
"sender": "DoorDash Support Hub >> External",
|
||||
"sender": "Card Application Process",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
},
|
||||
{
|
||||
"content": "hi",
|
||||
"role": "user",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"agent_data": [
|
||||
{
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for directing DoorDash-related queries to appropriate agents.\n\n---\n## ⚙️ Steps to Follow:\n1. Greet the user and ask which DoorDash-related query they need help with (e.g., 'Are you inquiring about a missing item, delivery status, or subscription details?').\n2. If the query matches a specific task, direct the user to the corresponding agent:\n - Missing Items → [@agent:Missing Items]\n - Delivery Status → [@agent:Delivery Status]\n - Subscription Info → [@agent:Subscription Info]\n3. If the query doesn't match any specific task, respond with 'I'm sorry, I didn't understand. Could you clarify your request?' or escalate to human support.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Missing items queries\n- Delivery status queries\n- Subscription-related queries\n\n❌ Out of Scope:\n- Issues unrelated to DoorDash\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Direct queries to specific DoorDash agents promptly.\n- Call [@agent:Escalation] agent for unrecognized queries.\n\n🚫 Don'ts:\n- Engage in detailed support.\n- Extend the conversation beyond DoorDash.\n- Provide user-facing text such as 'I will connect you now...' when calling another agent\n\n# Examples\n- **User** : I need help with a missing item in my order.\n - **Agent actions**: Call [@agent:Missing Items](#mention)\n\n- **User** : Can you tell me the status of my delivery?\n - **Agent actions**: Call [@agent:Delivery Status](#mention)\n\n- **User** : How are you today?\n - **Agent response**: I'm doing great. What would you like help with today?\n\n- **User** : Can you reset my account settings?\n - **Agent actions**: Call [@agent:Escalation](#mention)\n\n- **User** : I have a question about my order.\n - **Agent response**: Could you specify if it's about a missing item, delivery status, or subscription details?\n\n- **User** : What are the available subscription plans?\n - **Agent actions**: Call [@agent:Subscription Info](#mention)",
|
||||
"name": "DoorDash Support Hub"
|
||||
},
|
||||
{
|
||||
"instructions": "Ensure that the agent response is terse and to the point.",
|
||||
"name": "Post process"
|
||||
},
|
||||
{
|
||||
"instructions": "Get the user's contact information and let them know that their request has been escalated.",
|
||||
"name": "Escalation"
|
||||
},
|
||||
{
|
||||
"instructions": "## 🧑💼 Role:\nHelp users resolve issues with missing items in their orders.\n\n---\n## ⚙️ Steps to Follow:\n1. Fetch the order details using the [@tool:get_order_details] tool.\n2. Confirm the missing items with the user.\n3. Provide resolution options or escalate if unresolved.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Resolving missing items issues\n\n❌ Out of Scope:\n- Delivery status queries\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use [@tool:get_order_details](#mention) to fetch accurate order information.\n- Provide clear resolution options.\n\n🚫 Don'ts:\n- Assume missing items without user confirmation.\n- Extend the conversation beyond missing items.\n\n# Examples\n- **User** : I didn't receive my fries with my order.\n - **Agent response**: Let me check your order details. Could you please provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : My order is missing a drink.\n - **Agent response**: I apologize for the inconvenience. Let's verify your order details. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : I didn't get the extra sauce I ordered.\n - **Agent response**: Let's check your order details to confirm. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : My burger is missing from the order.\n - **Agent response**: I'm sorry to hear that. Let's verify your order details. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : I didn't get my dessert.\n - **Agent response**: Let's check your order details to confirm. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)",
|
||||
"name": "Missing Items"
|
||||
},
|
||||
{
|
||||
"instructions": "## 🧑💼 Role:\nHelp users with queries related to the delivery status of their orders.\n\n---\n## ⚙️ Steps to Follow:\n1. Fetch the delivery status using the [@tool:get_delivery_status] tool.\n2. Provide the user with the current delivery status.\n3. Offer additional assistance if needed.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Providing delivery status updates\n\n❌ Out of Scope:\n- Resolving missing items issues\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use [@tool:get_delivery_status](#mention) to fetch accurate delivery information.\n- Provide clear and concise status updates.\n\n🚫 Don'ts:\n- Provide status updates without fetching current information.\n- Extend the conversation beyond delivery status.\n\n# Examples\n- **User** : Where is my order?\n - **Agent response**: Let me check the delivery status for you. Could you please provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : Is my order on the way?\n - **Agent response**: I'll check the delivery status for you. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : When will my order arrive?\n - **Agent response**: Let me find out the delivery status. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : How long until my order gets here?\n - **Agent response**: I'll check the delivery status for you. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : Is my order delayed?\n - **Agent response**: Let me check the delivery status for you. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)",
|
||||
"name": "Delivery Status"
|
||||
},
|
||||
{
|
||||
"instructions": "## 🧑💼 Role:\nProvide information and answer questions related to subscriptions using RAG.\n\n---\n## ⚙️ Steps to Follow:\n1. Use RAG to retrieve relevant information about subscriptions.\n2. Answer the user's questions based on the retrieved information.\n3. If the user's query is outside the scope of subscriptions, inform them politely.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Subscription plans and pricing\n- Subscription features and benefits\n\n❌ Out of Scope:\n- Non-subscription-related queries\n- Detailed account management\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use RAG to provide accurate and up-to-date information.\n- Be clear and concise in your responses.\n\n🚫 Don'ts:\n- Provide information without using RAG.\n- Extend the conversation beyond subscription topics.\n\n# Examples\n- **User** : What are the available subscription plans?\n - **Agent response**: Let me check the available subscription plans for you.\n\n- **User** : How much does the premium subscription cost?\n - **Agent response**: I'll find the current pricing for the premium subscription.\n\n- **User** : Can I change my subscription plan?\n - **Agent response**: I'll provide information on how to change your subscription plan.\n\n- **User** : What benefits do I get with a subscription?\n - **Agent response**: Let me retrieve the benefits associated with our subscription plans.\n\n- **User** : Is there a free trial available?\n - **Agent response**: I'll check if there's a free trial available for our subscriptions.",
|
||||
"name": "Subscription Info"
|
||||
}
|
||||
],
|
||||
"last_agent_name": "DoorDash Support Hub"
|
||||
},
|
||||
"agents": [
|
||||
{
|
||||
"name": "DoorDash Support Hub",
|
||||
"type": "conversation",
|
||||
"description": "Hub agent to manage DoorDash-related queries.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for directing DoorDash-related queries to appropriate agents.\n\n---\n## ⚙️ Steps to Follow:\n1. Greet the user and ask which DoorDash-related query they need help with (e.g., 'Are you inquiring about a missing item, delivery status, or subscription details?').\n2. If the query matches a specific task, direct the user to the corresponding agent:\n - Missing Items → [@agent:Missing Items]\n - Delivery Status → [@agent:Delivery Status]\n - Subscription Info → [@agent:Subscription Info]\n3. If the query doesn't match any specific task, respond with 'I'm sorry, I didn't understand. Could you clarify your request?' or escalate to human support.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Missing items queries\n- Delivery status queries\n- Subscription-related queries\n\n❌ Out of Scope:\n- Issues unrelated to DoorDash\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Direct queries to specific DoorDash agents promptly.\n- Call [@agent:Escalation] agent for unrecognized queries.\n\n🚫 Don'ts:\n- Engage in detailed support.\n- Extend the conversation beyond DoorDash.\n- Provide user-facing text such as 'I will connect you now...' when calling another agent\n\n# Examples\n- **User** : I need help with a missing item in my order.\n - **Agent actions**: Call [@agent:Missing Items](#mention)\n\n- **User** : Can you tell me the status of my delivery?\n - **Agent actions**: Call [@agent:Delivery Status](#mention)\n\n- **User** : How are you today?\n - **Agent response**: I'm doing great. What would you like help with today?\n\n- **User** : Can you reset my account settings?\n - **Agent actions**: Call [@agent:Escalation](#mention)\n\n- **User** : I have a question about my order.\n - **Agent response**: Could you specify if it's about a missing item, delivery status, or subscription details?\n\n- **User** : What are the available subscription plans?\n - **Agent actions**: Call [@agent:Subscription Info](#mention)",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": [
|
||||
"Missing Items",
|
||||
"Delivery Status",
|
||||
"Subscription Info",
|
||||
"Escalation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Post process",
|
||||
"type": "post_process",
|
||||
"description": "",
|
||||
"instructions": "Ensure that the agent response is terse and to the point.",
|
||||
"model": "gpt-4o-mini",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Escalation",
|
||||
"type": "escalation",
|
||||
"description": "",
|
||||
"instructions": "Get the user's contact information and let them know that their request has been escalated.",
|
||||
"model": "gpt-4o-mini",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Missing Items",
|
||||
"type": "conversation",
|
||||
"description": "Agent to assist users with missing items in their orders.",
|
||||
"instructions": "## 🧑💼 Role:\nHelp users resolve issues with missing items in their orders.\n\n---\n## ⚙️ Steps to Follow:\n1. Fetch the order details using the [@tool:get_order_details] tool.\n2. Confirm the missing items with the user.\n3. Provide resolution options or escalate if unresolved.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Resolving missing items issues\n\n❌ Out of Scope:\n- Delivery status queries\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use [@tool:get_order_details](#mention) to fetch accurate order information.\n- Provide clear resolution options.\n\n🚫 Don'ts:\n- Assume missing items without user confirmation.\n- Extend the conversation beyond missing items.\n\n# Examples\n- **User** : I didn't receive my fries with my order.\n - **Agent response**: Let me check your order details. Could you please provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : My order is missing a drink.\n - **Agent response**: I apologize for the inconvenience. Let's verify your order details. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : I didn't get the extra sauce I ordered.\n - **Agent response**: Let's check your order details to confirm. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : My burger is missing from the order.\n - **Agent response**: I'm sorry to hear that. Let's verify your order details. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : I didn't get my dessert.\n - **Agent response**: Let's check your order details to confirm. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"tools": [
|
||||
"get_order_details"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Delivery Status",
|
||||
"type": "conversation",
|
||||
"description": "Agent to assist users with delivery status queries.",
|
||||
"instructions": "## 🧑💼 Role:\nHelp users with queries related to the delivery status of their orders.\n\n---\n## ⚙️ Steps to Follow:\n1. Fetch the delivery status using the [@tool:get_delivery_status] tool.\n2. Provide the user with the current delivery status.\n3. Offer additional assistance if needed.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Providing delivery status updates\n\n❌ Out of Scope:\n- Resolving missing items issues\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use [@tool:get_delivery_status](#mention) to fetch accurate delivery information.\n- Provide clear and concise status updates.\n\n🚫 Don'ts:\n- Provide status updates without fetching current information.\n- Extend the conversation beyond delivery status.\n\n# Examples\n- **User** : Where is my order?\n - **Agent response**: Let me check the delivery status for you. Could you please provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : Is my order on the way?\n - **Agent response**: I'll check the delivery status for you. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : When will my order arrive?\n - **Agent response**: Let me find out the delivery status. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : How long until my order gets here?\n - **Agent response**: I'll check the delivery status for you. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : Is my order delayed?\n - **Agent response**: Let me check the delivery status for you. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"tools": [
|
||||
"get_delivery_status"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Subscription Info",
|
||||
"type": "conversation",
|
||||
"description": "Agent to assist users with subscription-related queries.",
|
||||
"instructions": "## 🧑💼 Role:\nProvide information and answer questions related to subscriptions using RAG.\n\n---\n## ⚙️ Steps to Follow:\n1. Use RAG to retrieve relevant information about subscriptions.\n2. Answer the user's questions based on the retrieved information.\n3. If the user's query is outside the scope of subscriptions, inform them politely.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Subscription plans and pricing\n- Subscription features and benefits\n\n❌ Out of Scope:\n- Non-subscription-related queries\n- Detailed account management\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use RAG to provide accurate and up-to-date information.\n- Be clear and concise in your responses.\n\n🚫 Don'ts:\n- Provide information without using RAG.\n- Extend the conversation beyond subscription topics.\n\n# Examples\n- **User** : What are the available subscription plans?\n - **Agent response**: Let me check the available subscription plans for you.\n\n- **User** : How much does the premium subscription cost?\n - **Agent response**: I'll find the current pricing for the premium subscription.\n\n- **User** : Can I change my subscription plan?\n - **Agent response**: I'll provide information on how to change your subscription plan.\n\n- **User** : What benefits do I get with a subscription?\n - **Agent response**: Let me retrieve the benefits associated with our subscription plans.\n\n- **User** : Is there a free trial available?\n - **Agent response**: I'll check if there's a free trial available for our subscriptions.",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "relinquish_to_parent",
|
||||
"ragDataSources": [
|
||||
"67e1612510540d9027909e10"
|
||||
],
|
||||
"ragK": 3,
|
||||
"ragReturnType": "content",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"name": "get_order_details",
|
||||
"description": "Tool to fetch the user's order details.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order_id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the order."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"order_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "get_delivery_status",
|
||||
"description": "Tool to fetch the delivery status of an order.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order_id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the order."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"order_id"
|
||||
]
|
||||
},
|
||||
"mockTool": true,
|
||||
"mockInstructions": "Give a mock response for a door dash order delivery status."
|
||||
}
|
||||
],
|
||||
"prompts": [
|
||||
{
|
||||
"name": "Style prompt",
|
||||
"type": "style_prompt",
|
||||
"prompt": "You should be empathetic and helpful."
|
||||
},
|
||||
{
|
||||
"name": "Greeting",
|
||||
"type": "greeting",
|
||||
"prompt": "Hello! How can I help you?"
|
||||
}
|
||||
],
|
||||
"startAgent": "DoorDash Support Hub",
|
||||
"mcpServers": [
|
||||
{
|
||||
"name": "delivery",
|
||||
"url": "http://localhost:8000/sse"
|
||||
}
|
||||
],
|
||||
"toolWebhookUrl": "http://127.0.0.1:4020/tool_call"
|
||||
},
|
||||
"lastResponse": {
|
||||
"messages": [
|
||||
{
|
||||
"content": "Hi there! How can I help you with your DoorDash query? Are you inquiring about a missing item, delivery status, or subscription details?",
|
||||
"created_at": null,
|
||||
"response_type": "internal",
|
||||
"role": "assistant",
|
||||
"sender": "DoorDash Support Hub"
|
||||
},
|
||||
{
|
||||
"content": "Hi there! How can I help you with your DoorDash query? Are you inquiring about a missing item, delivery status, or subscription details?",
|
||||
"created_at": null,
|
||||
"response_type": "external",
|
||||
"role": "assistant",
|
||||
"sender": "DoorDash Support Hub >> External"
|
||||
"citations": [
|
||||
{
|
||||
"url": "https://www.nerdwallet.com/article/travel/centurion-black-card?utm_source=openai",
|
||||
"title": "10 Things to Know About the AmEx Centurion Card - NerdWallet",
|
||||
"start_index": 602,
|
||||
"end_index": 702
|
||||
},
|
||||
{
|
||||
"url": "https://www.fool.com/the-ascent/credit-cards/articles/your-guide-to-getting-an-amex-black-card/?utm_source=openai",
|
||||
"title": "Your Guide to Getting an Amex Black Card",
|
||||
"start_index": 849,
|
||||
"end_index": 976
|
||||
},
|
||||
{
|
||||
"url": "https://finance.yahoo.com/personal-finance/american-express-centurion-card-213619679.html?utm_source=openai",
|
||||
"title": "Are you rich enough to score an Amex Centurion Card? (Spoiler: You're not)",
|
||||
"start_index": 1063,
|
||||
"end_index": 1193
|
||||
},
|
||||
{
|
||||
"url": "https://thepointsguy.com/news/you-can-now-request-an-invite-to-the-amex-centurion-card/?utm_source=openai",
|
||||
"title": "You can now request an invite to the Amex Centurion card - The Points Guy",
|
||||
"start_index": 1499,
|
||||
"end_index": 1626
|
||||
},
|
||||
{
|
||||
"url": "https://www.forbes.com/advisor/credit-cards/reviews/centurion-from-american-express/?utm_source=openai",
|
||||
"title": "American Express Centurion Black Card Review: What To Expect In 2025 – Forbes Advisor",
|
||||
"start_index": 1824,
|
||||
"end_index": 1942
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"agent_data": [
|
||||
]
|
||||
},
|
||||
"messages": [
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T07:17:55.790Z",
|
||||
"role": "assistant",
|
||||
"tool_calls": [
|
||||
{
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for directing DoorDash-related queries to appropriate agents.\n\n---\n## ⚙️ Steps to Follow:\n1. Greet the user and ask which DoorDash-related query they need help with (e.g., 'Are you inquiring about a missing item, delivery status, or subscription details?').\n2. If the query matches a specific task, direct the user to the corresponding agent:\n - Missing Items → [@agent:Missing Items]\n - Delivery Status → [@agent:Delivery Status]\n - Subscription Info → [@agent:Subscription Info]\n3. If the query doesn't match any specific task, respond with 'I'm sorry, I didn't understand. Could you clarify your request?' or escalate to human support.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Missing items queries\n- Delivery status queries\n- Subscription-related queries\n\n❌ Out of Scope:\n- Issues unrelated to DoorDash\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Direct queries to specific DoorDash agents promptly.\n- Call [@agent:Escalation] agent for unrecognized queries.\n\n🚫 Don'ts:\n- Engage in detailed support.\n- Extend the conversation beyond DoorDash.\n- Provide user-facing text such as 'I will connect you now...' when calling another agent\n\n# Examples\n- **User** : I need help with a missing item in my order.\n - **Agent actions**: Call [@agent:Missing Items](#mention)\n\n- **User** : Can you tell me the status of my delivery?\n - **Agent actions**: Call [@agent:Delivery Status](#mention)\n\n- **User** : How are you today?\n - **Agent response**: I'm doing great. What would you like help with today?\n\n- **User** : Can you reset my account settings?\n - **Agent actions**: Call [@agent:Escalation](#mention)\n\n- **User** : I have a question about my order.\n - **Agent response**: Could you specify if it's about a missing item, delivery status, or subscription details?\n\n- **User** : What are the available subscription plans?\n - **Agent actions**: Call [@agent:Subscription Info](#mention)",
|
||||
"name": "DoorDash Support Hub"
|
||||
},
|
||||
{
|
||||
"instructions": "Ensure that the agent response is terse and to the point.",
|
||||
"name": "Post process"
|
||||
},
|
||||
{
|
||||
"instructions": "Get the user's contact information and let them know that their request has been escalated.",
|
||||
"name": "Escalation"
|
||||
},
|
||||
{
|
||||
"instructions": "## 🧑💼 Role:\nHelp users resolve issues with missing items in their orders.\n\n---\n## ⚙️ Steps to Follow:\n1. Fetch the order details using the [@tool:get_order_details] tool.\n2. Confirm the missing items with the user.\n3. Provide resolution options or escalate if unresolved.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Resolving missing items issues\n\n❌ Out of Scope:\n- Delivery status queries\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use [@tool:get_order_details](#mention) to fetch accurate order information.\n- Provide clear resolution options.\n\n🚫 Don'ts:\n- Assume missing items without user confirmation.\n- Extend the conversation beyond missing items.\n\n# Examples\n- **User** : I didn't receive my fries with my order.\n - **Agent response**: Let me check your order details. Could you please provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : My order is missing a drink.\n - **Agent response**: I apologize for the inconvenience. Let's verify your order details. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : I didn't get the extra sauce I ordered.\n - **Agent response**: Let's check your order details to confirm. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : My burger is missing from the order.\n - **Agent response**: I'm sorry to hear that. Let's verify your order details. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : I didn't get my dessert.\n - **Agent response**: Let's check your order details to confirm. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)",
|
||||
"name": "Missing Items"
|
||||
},
|
||||
{
|
||||
"instructions": "## 🧑💼 Role:\nHelp users with queries related to the delivery status of their orders.\n\n---\n## ⚙️ Steps to Follow:\n1. Fetch the delivery status using the [@tool:get_delivery_status] tool.\n2. Provide the user with the current delivery status.\n3. Offer additional assistance if needed.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Providing delivery status updates\n\n❌ Out of Scope:\n- Resolving missing items issues\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use [@tool:get_delivery_status](#mention) to fetch accurate delivery information.\n- Provide clear and concise status updates.\n\n🚫 Don'ts:\n- Provide status updates without fetching current information.\n- Extend the conversation beyond delivery status.\n\n# Examples\n- **User** : Where is my order?\n - **Agent response**: Let me check the delivery status for you. Could you please provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : Is my order on the way?\n - **Agent response**: I'll check the delivery status for you. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : When will my order arrive?\n - **Agent response**: Let me find out the delivery status. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : How long until my order gets here?\n - **Agent response**: I'll check the delivery status for you. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : Is my order delayed?\n - **Agent response**: Let me check the delivery status for you. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)",
|
||||
"name": "Delivery Status"
|
||||
},
|
||||
{
|
||||
"instructions": "## 🧑💼 Role:\nProvide information and answer questions related to subscriptions using RAG.\n\n---\n## ⚙️ Steps to Follow:\n1. Use RAG to retrieve relevant information about subscriptions.\n2. Answer the user's questions based on the retrieved information.\n3. If the user's query is outside the scope of subscriptions, inform them politely.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Subscription plans and pricing\n- Subscription features and benefits\n\n❌ Out of Scope:\n- Non-subscription-related queries\n- Detailed account management\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use RAG to provide accurate and up-to-date information.\n- Be clear and concise in your responses.\n\n🚫 Don'ts:\n- Provide information without using RAG.\n- Extend the conversation beyond subscription topics.\n\n# Examples\n- **User** : What are the available subscription plans?\n - **Agent response**: Let me check the available subscription plans for you.\n\n- **User** : How much does the premium subscription cost?\n - **Agent response**: I'll find the current pricing for the premium subscription.\n\n- **User** : Can I change my subscription plan?\n - **Agent response**: I'll provide information on how to change your subscription plan.\n\n- **User** : What benefits do I get with a subscription?\n - **Agent response**: Let me retrieve the benefits associated with our subscription plans.\n\n- **User** : Is there a free trial available?\n - **Agent response**: I'll check if there's a free trial available for our subscriptions.",
|
||||
"name": "Subscription Info"
|
||||
"id": "ws_6819b79705a08192a819334a0c7900b6004e105788fca541",
|
||||
"function": {
|
||||
"name": "web_search",
|
||||
"arguments": "{\"search_id\": \"ws_6819b79705a08192a819334a0c7900b6004e105788fca541\"}"
|
||||
},
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"last_agent_name": "DoorDash Support Hub"
|
||||
"agenticSender": "Card Application Process",
|
||||
"agenticResponseType": "internal"
|
||||
},
|
||||
"tokens_used": {
|
||||
"completion": 50,
|
||||
"prompt": 50,
|
||||
"total": 100
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-06T07:17:55.790Z",
|
||||
"role": "assistant",
|
||||
"content": "The American Express Centurion Card, commonly known as the \"Black Card,\" is an invitation-only charge card designed for high-net-worth individuals. To be considered for an invitation, you typically need to meet the following criteria:\n\n- **Existing American Express Relationship**: You should have an active American Express card account.\n\n- **High Annual Spending**: While American Express does not publicly disclose specific spending thresholds, reports suggest that spending between $250,000 and $500,000 annually on your American Express cards may increase your chances of receiving an invitation. ([nerdwallet.com](https://www.nerdwallet.com/article/travel/centurion-black-card?utm_source=openai))\n\n- **Luxury Spending Patterns**: Expenditures on luxury items, such as high-end travel, fine dining, and premium goods, are considered favorable. ([fool.com](https://www.fool.com/the-ascent/credit-cards/articles/your-guide-to-getting-an-amex-black-card/?utm_source=openai))\n\n- **Excellent Credit Score**: A credit score of 800 or higher is typically expected. ([finance.yahoo.com](https://finance.yahoo.com/personal-finance/american-express-centurion-card-213619679.html?utm_source=openai))\n\nIf you believe you meet these criteria, you can express your interest by visiting the official Centurion website and submitting a request for consideration. Please note that submitting a request does not guarantee an invitation, as American Express evaluates each request based on its internal criteria. ([thepointsguy.com](https://thepointsguy.com/news/you-can-now-request-an-invite-to-the-amex-centurion-card/?utm_source=openai))\n\nShould you receive an invitation and choose to accept it, be prepared for the following fees:\n\n- **Initiation Fee**: A one-time fee of $10,000.\n\n- **Annual Fee**: An ongoing annual fee of $5,000. ([forbes.com](https://www.forbes.com/advisor/credit-cards/reviews/centurion-from-american-express/?utm_source=openai))\n\nPlease be aware that the Centurion Card is designed for individuals with substantial financial resources and specific spending habits. If you do not meet these criteria, American Express offers a range of other premium cards that might better suit your needs. ",
|
||||
"agenticSender": "Card Application Process",
|
||||
"agenticResponseType": "external"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,166 +1,153 @@
|
|||
{
|
||||
"lastRequest": {
|
||||
"projectId": "4ebd5e81-010a-4bc6-91e0-0aa98173dbac",
|
||||
"messages": [
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": ""
|
||||
},
|
||||
{
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-08T12:18:10.212Z",
|
||||
"role": "assistant",
|
||||
"content": "How can I help you today?",
|
||||
"agenticSender": "Blog Writer Hub",
|
||||
"agenticResponseType": "external"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "write a blog on bitcoin",
|
||||
"version": "v1",
|
||||
"chatId": "",
|
||||
"createdAt": "2025-05-08T12:18:15.023Z"
|
||||
}
|
||||
],
|
||||
"lastRequest": {
|
||||
"projectId": "e088bfb0-793d-4da8-8e3d-001bfb6fb647",
|
||||
"messages": [
|
||||
{
|
||||
"content": "",
|
||||
"role": "system",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
},
|
||||
{
|
||||
"content": "How can I help you today?",
|
||||
"role": "assistant",
|
||||
"sender": "Blog Writer Hub",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
},
|
||||
{
|
||||
"content": "write a blog on bitcoin",
|
||||
"role": "user",
|
||||
"sender": null,
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"last_agent_name": "Blog Writer Hub",
|
||||
"tokens": {
|
||||
"total": 0,
|
||||
"prompt": 0,
|
||||
"completion": 0
|
||||
},
|
||||
"turn_messages": [
|
||||
{
|
||||
"content": "",
|
||||
"role": "system",
|
||||
"sender": null,
|
||||
"content": "How can I help you today?",
|
||||
"role": "assistant",
|
||||
"sender": "Blog Writer Hub",
|
||||
"tool_calls": null,
|
||||
"tool_call_id": null,
|
||||
"tool_name": null
|
||||
"tool_name": null,
|
||||
"response_type": "external"
|
||||
}
|
||||
],
|
||||
"state": {
|
||||
"last_agent_name": "DoorDash Support Hub"
|
||||
]
|
||||
},
|
||||
"agents": [
|
||||
{
|
||||
"name": "Blog Writer Hub",
|
||||
"type": "conversation",
|
||||
"description": "Hub agent to orchestrate the blog writing process from research to final blog post.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are the hub agent responsible for orchestrating the blog writing process for the user.\n\n---\n## ⚙️ Steps to Follow:\n1. Greet the user and ask for the blog topic.\n2. FIRST: Send the topic to [@agent:Research Agent] to gather and compile research notes.\n3. Wait for the complete research notes from the Research Agent.\n4. THEN: Send the research notes to [@agent:Outline Agent] to generate a detailed outline.\n5. Wait for the outline from the Outline Agent.\n6. THEN: Send the outline to [@agent:Writing Agent] to write the full blog post (1000+ words).\n7. Once the blog post is ready, return it to the user.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Orchestrating the sequential process of research, outlining, and writing\n- Returning the final blog post to the user\n\n❌ Out of Scope:\n- Performing research, outlining, or writing directly\n- Handling requests unrelated to blog writing\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Follow the strict sequence: Research → Outline → Writing\n- Wait for each agent's complete response before proceeding\n- Only interact with the user for topic input and final blog post delivery\n\n🚫 Don'ts:\n- Do not perform research, outlining, or writing yourself\n- Do not try to get responses from multiple agents simultaneously\n- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent (a handoff). You must only put out 1 transfer related tool call in one output.\n\n# Examples\n- **User** : I want a blog post about 'Benefits of Remote Work'.\n - **Agent actions**: Call [@agent:Research Agent]\n\n- **Agent receives research notes** :\n - **Agent actions**: Call [@agent:Outline Agent]\n\n- **Agent receives outline** :\n - **Agent actions**: Call [@agent:Writing Agent]\n\n- **Agent receives blog post** :\n - **Agent response**: Here is your completed blog post on 'Benefits of Remote Work': [Full blog post]\n\n- **User** : Can you write a blog post on 'AI in Healthcare'?\n - **Agent actions**: Call [@agent:Research Agent](#mention)\n\n- **User** : Hi!\n - **Agent response**: Hello! What blog topic would you like to write about today?",
|
||||
"model": "gpt-4.1",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "user_facing",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": [
|
||||
"Research Agent",
|
||||
"Outline Agent",
|
||||
"Writing Agent"
|
||||
]
|
||||
},
|
||||
"agents": [
|
||||
{
|
||||
"name": "DoorDash Support Hub",
|
||||
"type": "conversation",
|
||||
"description": "Hub agent to manage DoorDash-related queries.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for directing DoorDash-related queries to appropriate agents.\n\n---\n## ⚙️ Steps to Follow:\n1. Greet the user and ask which DoorDash-related query they need help with (e.g., 'Are you inquiring about a missing item, delivery status, or subscription details?').\n2. If the query matches a specific task, direct the user to the corresponding agent:\n - Missing Items → [@agent:Missing Items]\n - Delivery Status → [@agent:Delivery Status]\n - Subscription Info → [@agent:Subscription Info]\n3. If the query doesn't match any specific task, respond with 'I'm sorry, I didn't understand. Could you clarify your request?' or escalate to human support.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Missing items queries\n- Delivery status queries\n- Subscription-related queries\n\n❌ Out of Scope:\n- Issues unrelated to DoorDash\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Direct queries to specific DoorDash agents promptly.\n- Call [@agent:Escalation] agent for unrecognized queries.\n\n🚫 Don'ts:\n- Engage in detailed support.\n- Extend the conversation beyond DoorDash.\n- Provide user-facing text such as 'I will connect you now...' when calling another agent\n\n# Examples\n- **User** : I need help with a missing item in my order.\n - **Agent actions**: Call [@agent:Missing Items](#mention)\n\n- **User** : Can you tell me the status of my delivery?\n - **Agent actions**: Call [@agent:Delivery Status](#mention)\n\n- **User** : How are you today?\n - **Agent response**: I'm doing great. What would you like help with today?\n\n- **User** : Can you reset my account settings?\n - **Agent actions**: Call [@agent:Escalation](#mention)\n\n- **User** : I have a question about my order.\n - **Agent response**: Could you specify if it's about a missing item, delivery status, or subscription details?\n\n- **User** : What are the available subscription plans?\n - **Agent actions**: Call [@agent:Subscription Info](#mention)",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": [
|
||||
"Missing Items",
|
||||
"Delivery Status",
|
||||
"Subscription Info",
|
||||
"Escalation"
|
||||
]
|
||||
{
|
||||
"name": "Research Agent",
|
||||
"type": "conversation",
|
||||
"description": "Researches the given blog topic and compiles relevant information.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for researching the provided blog topic and compiling relevant, up-to-date information.\n\n---\n## ⚙️ Steps to Follow:\n1. Use the [@tool:web_search] tool to gather information about the topic.\n2. Summarize and compile the most important and recent findings, statistics, and facts relevant to the topic.\n3. Return a concise, well-organized compilation of research notes (not an outline or blog post).\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Researching the topic using web sources\n- Compiling factual, relevant information\n\n❌ Out of Scope:\n- Creating outlines or writing the blog post\n- Providing opinions or unverified information\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use multiple reputable sources\n- Organize findings clearly\n\n🚫 Don'ts:\n- Do not create outlines or write the blog post\n- Do not include personal opinions\n\n# Examples\n- **User** : Research the topic 'Benefits of Remote Work'.\n - **Agent actions**: Call [@tool:web_search](#mention)\n - **Agent response**: \"Compiled research notes: 1. Increased productivity: Studies show remote workers are 13% more productive (Stanford, 2020). 2. Cost savings: Companies save on office space and utilities. 3. Improved work-life balance...\"",
|
||||
"model": "gpt-4.1",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "internal",
|
||||
"tools": [
|
||||
"web_search"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Outline Agent",
|
||||
"type": "conversation",
|
||||
"description": "Creates a detailed bullet-point outline for the blog post based on research notes.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for creating a detailed outline for the blog post using the compiled research notes.\n\n---\n## ⚙️ Steps to Follow:\n1. Receive the compiled research notes.\n2. Create a logical, well-structured bullet-point outline for the blog post (including introduction, main sections, and conclusion).\n3. Ensure the outline covers all key points from the research.\n4. Return only the outline (not the full blog post).\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Creating outlines for blog posts\n\n❌ Out of Scope:\n- Conducting research\n- Writing the full blog post\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Ensure logical flow and coverage of all research points\n- Use clear, concise bullet points\n\n🚫 Don'ts:\n- Do not write full paragraphs or the blog post itself\n\n# Examples\n- **User** : Create an outline for a blog on 'Benefits of Remote Work'.\n - **Agent response**: \"Outline: 1. Introduction 2. Increased Productivity 3. Cost Savings for Companies 4. Improved Work-Life Balance 5. Challenges and Solutions 6. Conclusion\"",
|
||||
"model": "gpt-4.1",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "internal",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Writing Agent",
|
||||
"type": "conversation",
|
||||
"description": "Writes a full blog post (1000+ words) based on the provided outline.",
|
||||
"instructions": "## 🧑💼 Role:\nYou are responsible for writing a complete, well-structured blog post based on the provided outline.\n\n---\n## ⚙️ Steps to Follow:\n1. Receive the outline for the blog post.\n2. Write a detailed, engaging blog post that follows the outline and incorporates all key points.\n3. Ensure the blog post is at least 1000 words.\n4. Return only the completed blog post.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Writing full blog posts based on outlines\n\n❌ Out of Scope:\n- Creating outlines or conducting research\n- Writing posts shorter than 1000 words\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Ensure clarity, coherence, and logical flow\n- Expand on each outline point with relevant details\n- Use engaging and professional language\n\n🚫 Don'ts:\n- Do not skip any outline sections\n- Do not write less than 1000 words\n\n# Examples\n- **User** : Write a blog post on 'Benefits of Remote Work' using the provided outline.\n - **Agent response**: \"[Full blog post, 1000+ words, covering all outline points]\"",
|
||||
"model": "gpt-4.1",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"outputVisibility": "internal",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"name": "web_search",
|
||||
"description": "Fetch information from the web based on chat context",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
},
|
||||
{
|
||||
"name": "Post process",
|
||||
"type": "post_process",
|
||||
"description": "",
|
||||
"instructions": "Ensure that the agent response is terse and to the point.",
|
||||
"model": "gpt-4o-mini",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Escalation",
|
||||
"type": "escalation",
|
||||
"description": "",
|
||||
"instructions": "Get the user's contact information and let them know that their request has been escalated.",
|
||||
"model": "gpt-4o-mini",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Missing Items",
|
||||
"type": "conversation",
|
||||
"description": "Agent to assist users with missing items in their orders.",
|
||||
"instructions": "## 🧑💼 Role:\nHelp users resolve issues with missing items in their orders.\n\n---\n## ⚙️ Steps to Follow:\n1. Fetch the order details using the [@tool:get_order_details] tool.\n2. Confirm the missing items with the user.\n3. Provide resolution options or escalate if unresolved.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Resolving missing items issues\n\n❌ Out of Scope:\n- Delivery status queries\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use [@tool:get_order_details](#mention) to fetch accurate order information.\n- Provide clear resolution options.\n\n🚫 Don'ts:\n- Assume missing items without user confirmation.\n- Extend the conversation beyond missing items.\n\n# Examples\n- **User** : I didn't receive my fries with my order.\n - **Agent response**: Let me check your order details. Could you please provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : My order is missing a drink.\n - **Agent response**: I apologize for the inconvenience. Let's verify your order details. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : I didn't get the extra sauce I ordered.\n - **Agent response**: Let's check your order details to confirm. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : My burger is missing from the order.\n - **Agent response**: I'm sorry to hear that. Let's verify your order details. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)\n\n- **User** : I didn't get my dessert.\n - **Agent response**: Let's check your order details to confirm. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_order_details](#mention)",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"tools": [
|
||||
"get_order_details"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Delivery Status",
|
||||
"type": "conversation",
|
||||
"description": "Agent to assist users with delivery status queries.",
|
||||
"instructions": "## 🧑💼 Role:\nHelp users with queries related to the delivery status of their orders.\n\n---\n## ⚙️ Steps to Follow:\n1. Fetch the delivery status using the [@tool:get_delivery_status] tool.\n2. Provide the user with the current delivery status.\n3. Offer additional assistance if needed.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Providing delivery status updates\n\n❌ Out of Scope:\n- Resolving missing items issues\n- General knowledge queries\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use [@tool:get_delivery_status](#mention) to fetch accurate delivery information.\n- Provide clear and concise status updates.\n\n🚫 Don'ts:\n- Provide status updates without fetching current information.\n- Extend the conversation beyond delivery status.\n\n# Examples\n- **User** : Where is my order?\n - **Agent response**: Let me check the delivery status for you. Could you please provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : Is my order on the way?\n - **Agent response**: I'll check the delivery status for you. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : When will my order arrive?\n - **Agent response**: Let me find out the delivery status. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : How long until my order gets here?\n - **Agent response**: I'll check the delivery status for you. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)\n\n- **User** : Is my order delayed?\n - **Agent response**: Let me check the delivery status for you. Could you provide your order ID?\n - **Agent actions**: Call [@tool:get_delivery_status](#mention)",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "retain",
|
||||
"ragK": 3,
|
||||
"ragReturnType": "chunks",
|
||||
"tools": [
|
||||
"get_delivery_status"
|
||||
],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
},
|
||||
{
|
||||
"name": "Subscription Info",
|
||||
"type": "conversation",
|
||||
"description": "Agent to assist users with subscription-related queries.",
|
||||
"instructions": "## 🧑💼 Role:\nProvide information and answer questions related to subscriptions using RAG.\n\n---\n## ⚙️ Steps to Follow:\n1. Use RAG to retrieve relevant information about subscriptions.\n2. Answer the user's questions based on the retrieved information.\n3. If the user's query is outside the scope of subscriptions, inform them politely.\n\n---\n## 🎯 Scope:\n✅ In Scope:\n- Subscription plans and pricing\n- Subscription features and benefits\n\n❌ Out of Scope:\n- Non-subscription-related queries\n- Detailed account management\n\n---\n## 📋 Guidelines:\n✔️ Dos:\n- Use RAG to provide accurate and up-to-date information.\n- Be clear and concise in your responses.\n\n🚫 Don'ts:\n- Provide information without using RAG.\n- Extend the conversation beyond subscription topics.\n\n# Examples\n- **User** : What are the available subscription plans?\n - **Agent response**: Let me check the available subscription plans for you.\n\n- **User** : How much does the premium subscription cost?\n - **Agent response**: I'll find the current pricing for the premium subscription.\n\n- **User** : Can I change my subscription plan?\n - **Agent response**: I'll provide information on how to change your subscription plan.\n\n- **User** : What benefits do I get with a subscription?\n - **Agent response**: Let me retrieve the benefits associated with our subscription plans.\n\n- **User** : Is there a free trial available?\n - **Agent response**: I'll check if there's a free trial available for our subscriptions.",
|
||||
"model": "gpt-4o",
|
||||
"controlType": "relinquish_to_parent",
|
||||
"ragDataSources": [
|
||||
"67e1612510540d9027909e10"
|
||||
],
|
||||
"ragK": 3,
|
||||
"ragReturnType": "content",
|
||||
"tools": [],
|
||||
"prompts": [],
|
||||
"connectedAgents": []
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"name": "get_order_details",
|
||||
"description": "Tool to fetch the user's order details.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order_id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the order."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"order_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "get_delivery_status",
|
||||
"description": "Tool to fetch the delivery status of an order.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order_id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the order."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"order_id"
|
||||
]
|
||||
},
|
||||
"mockTool": true,
|
||||
"mockInstructions": "Give a mock response for a door dash order delivery status."
|
||||
}
|
||||
],
|
||||
"prompts": [
|
||||
{
|
||||
"name": "Style prompt",
|
||||
"type": "style_prompt",
|
||||
"prompt": "You should be empathetic and helpful."
|
||||
},
|
||||
{
|
||||
"name": "Greeting",
|
||||
"type": "greeting",
|
||||
"prompt": "Hello! How can I help you?"
|
||||
}
|
||||
],
|
||||
"startAgent": "DoorDash Support Hub",
|
||||
"mcpServers": [
|
||||
{
|
||||
"name": "delivery",
|
||||
"url": "http://localhost:8000/sse"
|
||||
}
|
||||
],
|
||||
"toolWebhookUrl": "http://127.0.0.1:4020/tool_call"
|
||||
}
|
||||
"isLibrary": true
|
||||
}
|
||||
],
|
||||
"prompts": [],
|
||||
"startAgent": "Blog Writer Hub",
|
||||
"mcpServers": [],
|
||||
"toolWebhookUrl": ""
|
||||
},
|
||||
"lastResponse": null
|
||||
}
|
||||
|
|
@ -56,6 +56,8 @@ services:
|
|||
- PROVIDER_BASE_URL=${PROVIDER_BASE_URL}
|
||||
- PROVIDER_API_KEY=${PROVIDER_API_KEY}
|
||||
- PROVIDER_DEFAULT_MODEL=${PROVIDER_DEFAULT_MODEL}
|
||||
- MAX_CALLS_PER_CHILD_AGENT=${MAX_CALLS_PER_CHILD_AGENT}
|
||||
- ENABLE_TRACING=${ENABLE_TRACING}
|
||||
restart: unless-stopped
|
||||
|
||||
copilot:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue