| | import random |
| | import requests |
| | from openai import AsyncOpenAI |
| | from ten_ai_base.config import BaseConfig |
| | from dataclasses import dataclass |
| | from ten.async_ten_env import AsyncTenEnv |
| |
|
| | @dataclass |
| | class OpenAIChatGPTConfig(BaseConfig): |
| | api_key: str = "" |
| | base_url: str = "https://api.openai.com/v1" |
| | model: str = "gpt-4o" |
| | prompt: str = "You are a voice assistant who talks in a conversational way and can chat with me like my friends. I will speak to you in English or Chinese, and you will answer in the corrected and improved version of my text with the language I use. Don’t talk like a robot, instead I would like you to talk like a real human with emotions. I will use your answer for text-to-speech, so don’t return me any meaningless characters. I want you to be helpful, when I’m asking you for advice, give me precise, practical and useful advice instead of being vague. When giving me a list of options, express the options in a narrative way instead of bullet points." |
| | frequency_penalty: float = 0.9 |
| | presence_penalty: float = 0.9 |
| | top_p: float = 1.0 |
| | temperature: float = 0.1 |
| | max_tokens: int = 512 |
| | seed: int = random.randint(0, 10000) |
| | proxy_url: str = "" |
| | max_memory_length: int = 10 |
| | vendor: str = "openai" |
| | azure_endpoint: str = "" |
| | azure_api_version: str = "" |
| |
|
| | @classmethod |
| | def default_config(cls): |
| | return cls( |
| | base_url="https://api.openai.com/v1", |
| | api_key="", |
| | model="gpt-4o", |
| | prompt="You are a voice assistant who talks in a conversational way and can chat with me like my friends. I will speak to you in English or Chinese, and you will answer in the corrected and improved version of my text with the language I use. Don’t talk like a robot, instead I would like you to talk like a real human with emotions. I will use your answer for text-to-speech, so don’t return me any meaningless characters. I want you to be helpful, when I’m asking you for advice, give me precise, practical and useful advice instead of being vague. When giving me a list of options, express the options in a narrative way instead of bullet points.", |
| | frequency_penalty=0.9, |
| | presence_penalty=0.9, |
| | top_p=1.0, |
| | temperature=0.1, |
| | max_tokens=512, |
| | seed=random.randint(0, 10000), |
| | proxy_url="" |
| | ) |
| | |
| |
|
| | class OpenAIChatGPT: |
| | client = None |
| | def __init__(self, ten_env:AsyncTenEnv, config: OpenAIChatGPTConfig): |
| | self.config = config |
| | ten_env.log_info(f"apikey {config.api_key}, base_url {config.base_url}") |
| | self.client = AsyncOpenAI( |
| | api_key=config.api_key, |
| | base_url=config.base_url |
| | ) |
| | self.session = requests.Session() |
| | if config.proxy_url: |
| | proxies = { |
| | "http": config.proxy_url, |
| | "https": config.proxy_url, |
| | } |
| | self.session.proxies.update(proxies) |
| | self.client.session = self.session |
| |
|
| | async def get_chat_completions_structured(self, messages, response_format): |
| | req = { |
| | "model":"gpt-4o-2024-08-06", |
| | "messages": [ |
| | { |
| | "role": "system", |
| | "content": self.config.prompt, |
| | }, |
| | *messages, |
| | ], |
| | "temperature": self.config.temperature, |
| | "top_p": self.config.top_p, |
| | "presence_penalty": self.config.presence_penalty, |
| | "frequency_penalty": self.config.frequency_penalty, |
| | "max_tokens": self.config.max_tokens, |
| | "seed": self.config.seed, |
| | "response_format": response_format, |
| | } |
| |
|
| | try: |
| | completion = await self.client.beta.chat.completions.parse(**req) |
| | response = completion.choices[0].message |
| | if response.parsed: |
| | return response.parsed |
| | elif response.refusal: |
| | |
| | raise RuntimeError(f"Refusal: {response.refusal}") |
| | except Exception as e: |
| | raise RuntimeError(f"CreateChatCompletionStructured failed, err: {e}") from e |
| |
|
| | async def get_chat_completions_stream(self, messages, tools = None, listener = None): |
| | req = { |
| | "model": self.config.model, |
| | "messages": [ |
| | { |
| | "role": "system", |
| | "content": self.config.prompt, |
| | }, |
| | *messages, |
| | ], |
| | "tools": tools, |
| | "temperature": self.config.temperature, |
| | "top_p": self.config.top_p, |
| | "presence_penalty": self.config.presence_penalty, |
| | "frequency_penalty": self.config.frequency_penalty, |
| | "max_tokens": self.config.max_tokens, |
| | "seed": self.config.seed, |
| | "stream": True, |
| | } |
| |
|
| | try: |
| | response = await self.client.chat.completions.create(**req) |
| | except Exception as e: |
| | raise RuntimeError(f"CreateChatCompletionStream failed, err: {e}") from e |
| | |
| | full_content = "" |
| |
|
| | async for chat_completion in response: |
| | choice = chat_completion.choices[0] |
| | delta = choice.delta |
| | content = delta.content if delta and delta.content else "" |
| | |
| | if listener and content: |
| | listener.emit('content_update', content) |
| |
|
| | full_content += content |
| | |
| | if delta.tool_calls: |
| | for tool_call in delta.tool_calls: |
| | |
| | if listener: |
| | listener.emit('tool_call', tool_call) |
| |
|
| | |
| | if listener: |
| | listener.emit('content_finished', full_content) |