diff --git a/infra/ai.yaml b/infra/ai.yaml index 08af4bf9..adcaf2e8 100644 --- a/infra/ai.yaml +++ b/infra/ai.yaml @@ -14,11 +14,11 @@ deployments: name: gpt-4o sku: name: "GlobalStandard" - capacity: 80 + capacity: 150 - name: gpt-4-evals model: format: OpenAI name: gpt-4o-mini sku: name: "GlobalStandard" - capacity: 80 + capacity: 150 diff --git a/src/api/agents/researcher/researcher.prompty b/src/api/agents/researcher/researcher.prompty index b98918a5..6e58b193 100644 --- a/src/api/agents/researcher/researcher.prompty +++ b/src/api/agents/researcher/researcher.prompty @@ -98,11 +98,6 @@ Return the results as a list of JSON objects with the following structure: - It's important that you only return the json object, nothing else!! Do not return any additional text. - Make sure to return at least 4 articles, but no more than 5. -# Feedback -Use this feedback to help you refine your queries and responses - if there is any feedback: - -{{feedback}} - # Market Codes The following are the market codes for the countries and regions that are supported by the Microsoft Bing API and should be used when formulating your queries. Use the language @@ -127,5 +122,11 @@ Swedish, sv-SE Turkish, tr-TR English, en-US +assistant: +# Feedback +Use this feedback to help you refine your queries and responses - if there is any feedback: + +{{feedback}} + user: -{{instructions}} \ No newline at end of file +{{instructions}} diff --git a/src/api/agents/researcher/researcher.py b/src/api/agents/researcher/researcher.py index 7f0af8f9..0ff35284 100644 --- a/src/api/agents/researcher/researcher.py +++ b/src/api/agents/researcher/researcher.py @@ -19,106 +19,131 @@ # At the moment, it should be in the format ";;;" # Customer needs to login to Azure subscription via Azure CLI and set the environment variables - -@trace -def execute_research(instructions: str, feedback: str = "No feedback"): - - ai_project_conn_str = os.getenv("AZURE_LOCATION")+".api.azureml.ms;"+os.getenv("AZURE_SUBSCRIPTION_ID")+";"+os.getenv("AZURE_RESOURCE_GROUP")+";"+os.getenv("AZURE_AI_PROJECT_NAME") - - project_client = AIProjectClient.from_connection_string( - credential=DefaultAzureCredential(), - conn_str=ai_project_conn_str, +ai_project_conn_str = os.getenv("AZURE_LOCATION")+".api.azureml.ms;"+os.getenv("AZURE_SUBSCRIPTION_ID")+";"+os.getenv("AZURE_RESOURCE_GROUP")+";"+os.getenv("AZURE_AI_PROJECT_NAME") +print(f"Connection string: {ai_project_conn_str}") + +project_client = AIProjectClient.from_connection_string( + credential=DefaultAzureCredential(), + conn_str=ai_project_conn_str, +) +print(f"Project client created: {project_client}") + +prompt_template = PromptTemplate.from_prompty(file_path="researcher.prompty") + +bing_connection = project_client.connections.get( + connection_name='bing-connection' +) +conn_id = bing_connection.id + +# Initialize agent bing tool and add the connection id +bing = BingGroundingTool(connection_id=conn_id) + +# Extract instructions from system message in Prompty template +messages = prompt_template.create_messages(instructions='', feedback='') +system_messages = [m for m in messages if m['role'] == 'system'] +agent_system_instructions = system_messages[0]['content'] + +AGENT_NAME = "contoso-creative-writer" +found_agent = None +all_agents_list = project_client.agents.list_agents().data +for a in all_agents_list: + if a.name == AGENT_NAME: + found_agent = a + break + +model_name = "gpt-4" +if found_agent: + # Update the existing agent to use new tools + agent = project_client.agents.update_agent( + assistant_id=found_agent.id, + model=model_name, + instructions=agent_system_instructions, + tools=bing.definitions, ) - - prompt_template = PromptTemplate.from_prompty(file_path="researcher.prompty") - - - instructions = instructions - feedback= feedback - messages = prompt_template.create_messages(instructions=instructions, feedback=feedback) - - bing_connection = project_client.connections.get( - connection_name='bing-connection' + print(f"reusing agent > {agent.name} (id: {agent.id})") +else: + agent = project_client.agents.create_agent( + model=model_name, + name=AGENT_NAME, + instructions=agent_system_instructions, + tools=bing.definitions, ) - conn_id = bing_connection.id - - # Initialize agent bing tool and add the connection id - bing = BingGroundingTool(connection_id=conn_id) + print(f"creating agent > {agent.name} (id: {agent.id})") - prompt_template = PromptTemplate.from_prompty(file_path="researcher.prompty") +@trace +def execute_research(instructions: str, feedback: str = None): - # Create agent with the bing tool and process assistant run - with project_client: - agent = project_client.agents.create_agent( - model="gpt-4", - name="my-assistant", - instructions=messages[0]['content'], - tools=bing.definitions, - ) + if not feedback: + feedback = "No feedback" - print(f"Created agent, ID: {agent.id}") + # Create thread for communication + thread = project_client.agents.create_thread() + print(f"Created thread, ID: {thread.id}") - # Create thread for communication - thread = project_client.agents.create_thread() - print(f"Created thread, ID: {thread.id}") + # Create assistant and user messages from Prompty template + messages = prompt_template.create_messages(instructions=instructions, feedback=feedback) + thread_messages = [m for m in messages if m['role'] in ['assistant', 'user']] + for message in thread_messages: + content = message['content'] + role = message['role'] # Create message to thread message = project_client.agents.create_message( thread_id=thread.id, - role="user", - content=instructions, - ) - print(f"Created message, ID: {message.id}") - - # # Create and process agent run in thread with tools - # run = project_client.agents.create_stream(thread_id=thread.id, assistant_id=agent.id) - def is_rate_limited(run): - # Check if the run failed due to rate limit - if run.status == "failed" and run.last_error and run.last_error.get('code') == 'rate_limit_exceeded': - print(f"Run failed: {run.last_error}") - return True # Indicates Tenacity should retry - return False # No retry needed - - @retry( - retry=retry_if_result(is_rate_limited), - wait=wait_exponential(multiplier=1, min=4, max=60), - stop=stop_after_attempt(6) + role=role, + content=content, ) - def run_agent(): - # Create and process agent run in thread with tools - run = project_client.agents.create_and_process_run(thread_id=thread.id, assistant_id=agent.id) - print(f"Run finished with status: {run.status}") - return run - - run = run_agent() - # Retrieve run step details to get Bing Search query link - # To render the webpage, we recommend you replace the endpoint of Bing search query URLs with `www.bing.com` and your Bing search query URL would look like "https://www.bing.com/search?q={search query}" - run_steps = project_client.agents.list_run_steps(run_id=run.id, thread_id=thread.id) - run_steps_data = run_steps['data'] - - print(f"Agent created and now researching...") - print('') - - # Delete the assistant when done - project_client.agents.delete_agent(agent.id) - - # Fetch and log all messages - messages = project_client.agents.list_messages(thread_id=thread.id) - # print(f"Messages: {messages}") + print(f"Created {role} message, ID: {message.id}") + + # Create and process agent run in thread with tools + # run = project_client.agents.create_stream(thread_id=thread.id, assistant_id=agent.id) + def is_rate_limited(run): + # Check if the run failed due to rate limit + if run.status == "failed" and run.last_error and run.last_error.get('code') == 'rate_limit_exceeded': + print(f"Run failed: {run.last_error}") + return True # Indicates Tenacity should retry + return False # No retry needed + + @retry( + retry=retry_if_result(is_rate_limited), + wait=wait_exponential(multiplier=1, min=4, max=60), + stop=stop_after_attempt(6) + ) + def run_agent(): + # Create and process agent run in thread with tools + run = project_client.agents.create_and_process_run(thread_id=thread.id, assistant_id=agent.id) + print(f"Run finished with status: {run.status}") + return run + + run = run_agent() + # Retrieve run step details to get Bing Search query link + # To render the webpage, we recommend you replace the endpoint of Bing search query URLs with `www.bing.com` and your Bing search query URL would look like "https://www.bing.com/search?q={search query}" + run_steps = project_client.agents.list_run_steps(run_id=run.id, thread_id=thread.id) + run_steps_data = run_steps['data'] + + print(f"Agent created and now researching...") + print('') + + # Delete the assistant when done + #project_client.agents.delete_agent(agent.id) + + # Fetch and log all messages + messages = project_client.agents.list_messages(thread_id=thread.id) + # print(f"Messages: {messages}") + research_response = messages.data[0]['content'][0]['text']['value'] + try: + json_r = json.loads(research_response) + except: + print('retrying') research_response = messages.data[0]['content'][0]['text']['value'] - try: - json_r = json.loads(research_response) - except: - print('retrying') - research_response = messages.data[0]['content'][0]['text']['value'] - json_r = json.loads(research_response) - research = json_r['web'] - print('research succesfully completed') - return research + json_r = json.loads(research_response) + research = json_r['web'] + print('research succesfully completed') + return research @trace -def research(instructions: str, feedback: str = "No feedback"): - r = execute_research(instructions=instructions) +def research(instructions: str, feedback: str = None): + r = execute_research(instructions=instructions, feedback=feedback) research = { "web": r, "entities": [], diff --git a/src/api/orchestrator.py b/src/api/orchestrator.py index 0c48d57a..b0362fa8 100644 --- a/src/api/orchestrator.py +++ b/src/api/orchestrator.py @@ -8,7 +8,6 @@ from agents.researcher import researcher from agents.product import product from agents.writer import writer -# from agents.designer import designer from agents.editor import editor from evaluate.evaluators import evaluate_article_in_background from prompty.tracer import trace, Tracer, console_tracer, PromptyTracer @@ -68,12 +67,10 @@ def building_agents_message(): @trace def create(research_context, product_context, assignment_context, evaluate=False): - feedback = "No Feedback" - yield building_agents_message() yield start_message("researcher") - research_result = researcher.research(research_context, feedback) + research_result = researcher.research(research_context) yield complete_message("researcher", research_result) yield start_message("marketing") @@ -88,7 +85,7 @@ def create(research_context, product_context, assignment_context, evaluate=False product_context, product_result, assignment_context, - feedback, + feedback = "No Feedback", ) full_result = " " @@ -98,11 +95,6 @@ def create(research_context, product_context, assignment_context, evaluate=False processed_writer_result = writer.process(full_result) - # send article to the designer, to generate an image for the blog - # yield start_message("designer") - # designer_response = designer.design(processed_writer_result['article']) - # yield complete_message("designer", [f"Image stored in {designer_response}"]) - # Then send it to the editor, to decide if it's good or not yield start_message("editor") editor_response = editor.edit(processed_writer_result['article'], processed_writer_result["feedback"]) diff --git a/src/web/package.json b/src/web/package.json index 6e0ee420..7cb5ca9d 100644 --- a/src/web/package.json +++ b/src/web/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", + "dev": "vite --host", "build": "tsc && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview"