Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/openai/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies = [
"microsoft-teams-ai",
"microsoft-teams-common",
"openai>=1.102.0",
"python-dotenv",
]

[build-system]
Expand Down
39 changes: 38 additions & 1 deletion packages/openai/src/microsoft/teams/openai/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@

from dataclasses import dataclass, field
from logging import Logger
from os import getenv
from typing import Literal

from dotenv import find_dotenv, load_dotenv
from microsoft.teams.common.logging import ConsoleLogger

from openai import AsyncAzureOpenAI, AsyncOpenAI

load_dotenv(find_dotenv(usecwd=True))


@dataclass
class OpenAIBaseModel:
model: str
model: str | None = None
key: str | None = None
client: AsyncOpenAI | None = None
mode: Literal["completions", "responses"] = "responses"
Expand All @@ -24,8 +28,41 @@ class OpenAIBaseModel:
api_version: str | None = None
logger: Logger = field(default_factory=lambda: ConsoleLogger().create_logger(name="OpenAI-Model"))
_client: AsyncOpenAI = field(init=False)
_model: str = field(init=False)

def __post_init__(self):
# Get model from env if not provided
if self.model is None:
env_model = getenv("AZURE_OPENAI_MODEL") or getenv("OPENAI_MODEL")
if not env_model:
raise ValueError(
"Model is required. Set AZURE_OPENAI_MODEL/OPENAI_MODEL env var or provide model parameter."
)
else:
self._model = env_model
else:
self._model = self.model

# Get API key from env if not provided (and no client provided)
if self.client is None and self.key is None:
self.key = getenv("AZURE_OPENAI_API_KEY") or getenv("OPENAI_API_KEY")
if not self.key:
raise ValueError(
"API key is required. Set AZURE_OPENAI_API_KEY/OPENAI_API_KEY env var or provide key parameter."
)

# Get Azure endpoint from env if not provided
if self.azure_endpoint is None:
self.azure_endpoint = getenv("AZURE_OPENAI_ENDPOINT")

# Get API version from env if not provided
if self.api_version is None:
self.api_version = getenv("AZURE_OPENAI_API_VERSION")

# Get base URL from env if not provided
if self.base_url is None:
self.base_url = getenv("OPENAI_BASE_URL")

if self.client is None and self.key is None:
raise ValueError("Either key or client is required when initializing an OpenAIModel")
elif self.client is not None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ async def generate_text(

# Make OpenAI API call (with streaming if on_chunk provided)
response = await self._client.chat.completions.create(
model=self.model, messages=openai_messages, tools=tools, stream=bool(on_chunk)
model=self._model, messages=openai_messages, tools=tools, stream=bool(on_chunk)
)

# Convert response back to ModelMessage format
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ async def _send_stateful(

# Make OpenAI Responses API call
response = await self._client.responses.create(
model=self.model,
model=self._model,
input=responses_input,
instructions=system.content if system and system.content else None,
tools=tools,
Expand Down Expand Up @@ -175,7 +175,7 @@ async def _send_stateless(

# Make OpenAI Responses API call (stateless)
response = await self._client.responses.create(
model=self.model,
model=self._model,
input=responses_input,
instructions=system.content if system and system.content else NOT_GIVEN,
tools=tools,
Expand Down
33 changes: 2 additions & 31 deletions tests/ai-test/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,14 @@

import asyncio
import re
from os import getenv

from dotenv import find_dotenv, load_dotenv
from microsoft.teams.ai import Agent, Function, ListMemory, UserMessage
from microsoft.teams.api import MessageActivity
from microsoft.teams.apps import ActivityContext, App
from microsoft.teams.devtools import DevToolsPlugin
from microsoft.teams.openai import OpenAICompletionsAIModel, OpenAIResponsesAIModel
from pydantic import BaseModel

load_dotenv(find_dotenv(usecwd=True))


def get_required_env(key: str) -> str:
value = getenv(key)
if not value:
raise ValueError(f"Required environment variable {key} is not set")
return value


AZURE_OPENAI_API_KEY = get_required_env("AZURE_OPENAI_API_KEY")
AZURE_OPENAI_ENDPOINT = get_required_env("AZURE_OPENAI_ENDPOINT")
AZURE_OPENAI_MODEL = get_required_env("AZURE_OPENAI_MODEL")
AZURE_OPENAI_API_VERSION = get_required_env("AZURE_OPENAI_API_VERSION")

app = App(plugins=[DevToolsPlugin()])

# Global state for mode switching
Expand All @@ -40,20 +23,8 @@ class GetWeatherParams(BaseModel):
location: str


chat_openai_ai_model = OpenAICompletionsAIModel(
key=AZURE_OPENAI_API_KEY,
model=AZURE_OPENAI_MODEL,
azure_endpoint=AZURE_OPENAI_ENDPOINT,
api_version=AZURE_OPENAI_API_VERSION,
)

responses_openai_ai_model = OpenAIResponsesAIModel(
key=AZURE_OPENAI_API_KEY,
model=AZURE_OPENAI_MODEL,
azure_endpoint=AZURE_OPENAI_ENDPOINT,
api_version=AZURE_OPENAI_API_VERSION,
stateful=True,
)
chat_openai_ai_model = OpenAICompletionsAIModel()
responses_openai_ai_model = OpenAIResponsesAIModel(stateful=True)
chat_memory = ListMemory()

chat_agent = Agent(chat_openai_ai_model, memory=chat_memory)
Expand Down
24 changes: 1 addition & 23 deletions tests/mcp-client/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
"""

import asyncio
from os import getenv

from dotenv import find_dotenv, load_dotenv
from microsoft.teams.ai import Agent, ListMemory
from microsoft.teams.api import MessageActivity, TypingActivityInput
from microsoft.teams.apps import ActivityContext, App
Expand All @@ -15,28 +13,8 @@
from microsoft.teams.openai import OpenAIResponsesAIModel

app = App(plugins=[DevToolsPlugin()])
load_dotenv(find_dotenv(usecwd=True))


def get_required_env(key: str) -> str:
value = getenv(key)
if not value:
raise ValueError(f"Required environment variable {key} is not set")
return value


AZURE_OPENAI_API_KEY = get_required_env("AZURE_OPENAI_API_KEY")
AZURE_OPENAI_ENDPOINT = get_required_env("AZURE_OPENAI_ENDPOINT")
AZURE_OPENAI_MODEL = get_required_env("AZURE_OPENAI_MODEL")
AZURE_OPENAI_API_VERSION = get_required_env("AZURE_OPENAI_API_VERSION")

responses_openai_ai_model = OpenAIResponsesAIModel(
key=AZURE_OPENAI_API_KEY,
model=AZURE_OPENAI_MODEL,
azure_endpoint=AZURE_OPENAI_ENDPOINT,
api_version=AZURE_OPENAI_API_VERSION,
stateful=True,
)
responses_openai_ai_model = OpenAIResponsesAIModel(stateful=True)
chat_memory = ListMemory()
mcp_plugin = McpClientPlugin()
mcp_plugin.use_mcp_server("https://learn.microsoft.com/api/mcp")
Expand Down
2 changes: 2 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.