8000 Weather openAI (single turn) (#18) · microsoft/Agents-for-python@eca4fce · GitHub
[go: up one dir, main page]

Skip to content

Commit eca4fce

Browse files
authored
Weather openAI (single turn) (#18)
* WIP Weather openAI * Single-turn weather openai working
1 parent b9e973a commit eca4fce

File tree

7 files changed

+208
-1
lines changed

7 files changed

+208
-1
lines changed

libraries/Builder/microsoft-agents-builder/microsoft/agents/builder/message_factory.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ def attachment_activity(
2525
type=ActivityTypes.message,
2626
attachment_layout=attachment_layout,
2727
attachments=attachments,
28-
input_hint=input_hint,
2928
)
3029
if text:
3130
message.text = text
3231
if speak:
3332
message.speak = speak
33+
if input_hint:
34+
message.input_hint = input_hint
3435
return message
3536

3637

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import os
5+
from agents import set_tracing_export_api_key
6+
from dotenv import load_dotenv
7+
from aiohttp.web import Application, Request, Response, run_app
8+
9+
from microsoft.agents.builder import RestChannelServiceClientFactory
10+
from microsoft.agents.hosting.aiohttp import CloudAdapter, jwt_authorization_middleware
11+
from microsoft.agents.authorization import (
12+
Connections,
13+
AccessTokenProviderBase,
14+
ClaimsIdentity,
15+
)
16+
from microsoft.agents.authentication.msal import MsalAuth
17+
from openai import AsyncAzureOpenAI
18+
19+
from weather_agent import WeatherAgent
20+
from config import DefaultConfig
21+
22+
load_dotenv()
23+
24+
CONFIG = DefaultConfig()
25+
AUTH_PROVIDER = MsalAuth(DefaultConfig())
26+
27+
28+
class DefaultConnection(Connections):
29+
def get_default_connection(self) -> AccessTokenProviderBase:
30+
pass
31+
32+
def get_token_provider(
33+
self, claims_identity: ClaimsIdentity, service_url: str
34+
) -> AccessTokenProviderBase:
35+
return AUTH_PROVIDER
36+
37+
def get_connection(self, connection_name: str) -> AccessTokenProviderBase:
38+
pass
39+
40+
41+
CHANNEL_CLIENT_FACTORY = RestChannelServiceClientFactory(CONFIG, DefaultConnection())
42+
43+
# Create adapter.
44+
ADAPTER = CloudAdapter(CHANNEL_CLIENT_FACTORY)
45+
46+
# gets the API Key from environment variable AZURE_OPENAI_API_KEY
47+
CLIENT = AsyncAzureOpenAI(
48+
api_version=CONFIG.AZURE_OPENAI_API_VERSION,
49+
azure_endpoint=CONFIG.AZURE_OPENAI_ENDPOINT,
50+
)
51+
52+
# Create the Agent
53+
AGENT = WeatherAgent(client=CLIENT)
54+
55+
56+
# Listen for incoming requests on /api/messages
57+
async def messages(req: Request) -> Response:
58+
adapter: CloudAdapter = req.app["adapter"]
59+
return await adapter.process(req, AGENT)
60+
61+
62+
APP = Application(middlewares=[jwt_authorization_middleware])
63+
APP.router.add_post("/api/messages", messages)
64+
APP["agent_configuration"] = CONFIG
65+
APP["adapter"] = ADAPTER
66+
67+
if __name__ == "__main__":
68+
try:
69+
run_app(APP, host="localhost", port=CONFIG.PORT)
70+
except Exception as error:
71+
raise error
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from os import environ
2+
from microsoft.agents.authentication.msal import AuthTypes, MsalAuthConfiguration
3+
4+
5+
class DefaultConfig(MsalAuthConfiguration):
6+
"""Agent Configuration"""
7+
8+
def __init__(self) -> None:
9+
self.AUTH_TYPE = AuthTypes.client_secret
10+
self.TENANT_ID = "" or environ.get("TENANT_ID")
11+
self.CLIENT_ID = "" or environ.get("CLIENT_ID")
12+
self.CLIENT_SECRET = "" or environ.get("CLIENT_SECRET")
13+
self.AZURE_OPENAI_API_KEY = "" or environ.get("AZURE_OPENAI_API_KEY")
14+
self.AZURE_OPENAI_ENDPOINT = "" or environ.get("AZURE_OPENAI_ENDPOINT")
15+
self.AZURE_OPENAI_API_VERSION = "" or environ.get(
16+
"AZURE_OPENAI_API_VERSION", "2024-06-01"
17+
)
18+
self.PORT = 3978
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
openai
2+
openai-agents
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from agents import function_tool
2+
from datetime import datetime
3+
4+
5+
@function_tool
6+
def get_date() -> str:
7+
"""
8+
A function tool that returns the current date and time.
9+
"""
10+
return datetime.now().isoformat()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import random
2+
from pydantic import BaseModel
3+
4+
from agents import function_tool
5+
6+
7+
class Weather(BaseModel):
8+
city: str
9+
temperature: str
10+
conditions: str
11+
date: str
12+
13+
14+
@function_tool
15+
def get_weather(city: str, date: str) -> Weather:
16+
print("[debug] get_weather called")
17+
temperature = random.randint(8, 21)
18+
return Weather(
19+
city=city,
20+
temperature=f"{temperature}C",
21+
conditions="Sunny with wind.",
22+
date=date,
23+
)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from __future__ import annotations
2+
3+
from typing import Union
4+
from microsoft.agents.builder import ActivityHandler, MessageFactory, TurnContext
5+
from microsoft.agents.core.models import ChannelAccount, Attachment
6+
7+
from agents import (
8+
Agent as OpenAIAgent,
9+
Model,
10+
ModelProvider,
11+
OpenAIChatCompletionsModel,
12+
RunConfig,
13+
Runner,
14+
)
15+
from openai import AsyncAzureOpenAI
16+
from pydantic import BaseModel, Field
17+
18+
from tools.get_weather_tool import get_weather
19+
from tools.date_time_tool import get_date
20+
21+
22+
class WeatherForecastAgentResponse(BaseModel):
23+
contentType: str = Field(pattern=r"^(Text|AdaptiveCard)$")
24+
content: Union[dict, str]
25+
26+
27+
class WeatherAgent(ActivityHandler):
28+
def __init__(self, client: AsyncAzureOpenAI):
29+
self.agent = OpenAIAgent(
30+
name="WeatherAgent",
31+
instructions=""""
32+
You are a friendly assistant that helps people find a weather forecast for a given time and place.
33+
Do not reply with MD format nor plain text. You can ONLY respond in JSON format with the following JSON schema
34+
{
35+
"contentType": "'Text' if you don't have a forecast or 'AdaptiveCard' if you do",
36+
"content": "{The content of the response, may be plain text, or JSON based adaptive card}"
37+
}
38+
You may ask follow up questions until you have enough information to answer the customers question,
39+
but once you have a forecast forecast, make sure to format it nicely using an adaptive card.
40+
""",
41+
tools=[get_weather, get_date],
42+
)
43+
44+
class CustomModelProvider(ModelProvider):
45+
def get_model(self, model_name: str | None) -> Model:
46+
return OpenAIChatCompletionsModel(
47+
model=model_name or "gpt-4o", openai_client=client
48+
)
49+
50+
self.custom_model_provider = CustomModelProvider()
51+
52+
async def on_members_added_activity(
53+
self, members_added: list[ChannelAccount], turn_context: TurnContext
54+
):
55+
for member in members_added:
56+
if member.id != turn_context.activity.recipient.id:
57+
await turn_context.send_activity("Hello and welcome!")
58+
59+
async def on_message_activity(self, turn_context: TurnContext):
60+
response = await Runner.run(
61+
self.agent,
62+
turn_context.activity.text,
63+
run_config=RunConfig(
64+
model_provider=self.custom_model_provider,
65+
tracing_disabled=True,
66+
),
67+
)
68+
69+
llm_response = WeatherForecastAgentResponse.model_validate_json(
70+
response.final_output
71+
)
72+
if llm_response.contentType == "AdaptiveCard":
73+
activity = MessageFactory.attachment(
74+
Attachment(
75+
content_type="application/vnd.microsoft.card.adaptive",
76+
content=llm_response.content,
77+
)
78+
)
79+
elif llm_response.contentType == "Text":
80+
activity = MessageFactory.text(llm_response.content)
81+
82+
return await turn_context.send_activity(activity)

0 commit comments

Comments
 (0)
0