From 46896cea06c480d5763e6b981f66801b7a02af42 Mon Sep 17 00:00:00 2001 From: zhuming Date: Tue, 24 Dec 2024 14:12:58 +0800 Subject: [PATCH 1/4] dev: add __main__.py to ghostos --- ghostos/__main__.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 ghostos/__main__.py diff --git a/ghostos/__main__.py b/ghostos/__main__.py new file mode 100644 index 00000000..8277184b --- /dev/null +++ b/ghostos/__main__.py @@ -0,0 +1,4 @@ +from ghostos.scripts.cli import main + +if __name__ == "__main__": + main(prog_name="help") From 873d2460a833089a3e0aae9c759512b999710524 Mon Sep 17 00:00:00 2001 From: zhuming Date: Tue, 24 Dec 2024 15:40:04 +0800 Subject: [PATCH 2/4] dev: hide pyaudio dependency --- ghostos/framework/openai_realtime/__init__.py | 3 +- ghostos/framework/realtime/__init__.py | 2 +- ghostos/framework/realtime/defaults.py | 13 ++++-- .../streamlitapp/pages/chat_with_ghost.py | 45 ++++++++++++------- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/ghostos/framework/openai_realtime/__init__.py b/ghostos/framework/openai_realtime/__init__.py index 2dfc3f24..6f02c07a 100644 --- a/ghostos/framework/openai_realtime/__init__.py +++ b/ghostos/framework/openai_realtime/__init__.py @@ -1,7 +1,6 @@ from ghostos.abcd import Conversation from ghostos.abcd.realtime import RealtimeApp from ghostos.abcd.realtime import Speaker, Listener -from ghostos.framework.openai_realtime.driver import OpenAIRealtimeDriver def get_openai_realtime_app( @@ -11,7 +10,7 @@ def get_openai_realtime_app( speaker: Speaker, listener: Listener, ) -> RealtimeApp: - + from ghostos.framework.openai_realtime.driver import OpenAIRealtimeDriver from ghostos.framework.openai_realtime.app import RealtimeAppImpl from ghostos.framework.openai_realtime.configs import OpenAIRealtimeAppConf from ghostos.framework.openai_realtime.ws import OpenAIWSConnection diff --git a/ghostos/framework/realtime/__init__.py b/ghostos/framework/realtime/__init__.py index 4c48f0ec..5ca2182e 100644 --- a/ghostos/framework/realtime/__init__.py +++ b/ghostos/framework/realtime/__init__.py @@ -1,2 +1,2 @@ -from ghostos.abcd.realtime import Realtime, RealtimeConfig +from ghostos.abcd.realtime import Realtime, RealtimeConfig, RealtimeApp, RealtimeDriver from ghostos.framework.realtime.defaults import ConfigBasedRealtimeProvider diff --git a/ghostos/framework/realtime/defaults.py b/ghostos/framework/realtime/defaults.py index dc9d273e..c08149b6 100644 --- a/ghostos/framework/realtime/defaults.py +++ b/ghostos/framework/realtime/defaults.py @@ -6,9 +6,9 @@ Realtime, RealtimeConfig, RealtimeDriver, Listener, Speaker, RealtimeAppConfig, RealtimeApp, ) +from ghostos.contracts.logger import LoggerItf from ghostos.contracts.configs import YamlConfig, Configs from ghostos.container import Container, Provider, INSTANCE -from ghostos.framework.openai_realtime import OpenAIRealtimeDriver class BasicRealtimeConfig(RealtimeConfig, YamlConfig): @@ -55,6 +55,13 @@ def singleton(self) -> bool: def factory(self, con: Container) -> Optional[INSTANCE]: configs = con.get(Configs) + logger = con.force_fetch(LoggerItf) realtime = ConfigsBasedRealtime(configs) - realtime.register(OpenAIRealtimeDriver()) - return realtime + try: + from ghostos.framework.openai_realtime.driver import OpenAIRealtimeDriver + realtime.register(OpenAIRealtimeDriver()) + except ImportError: + OpenAIRealtimeDriver = None + logger.info(f"failed to import OpenAIRealtimeDriver") + finally: + return realtime diff --git a/ghostos/prototypes/streamlitapp/pages/chat_with_ghost.py b/ghostos/prototypes/streamlitapp/pages/chat_with_ghost.py index b29b5381..02695fc0 100644 --- a/ghostos/prototypes/streamlitapp/pages/chat_with_ghost.py +++ b/ghostos/prototypes/streamlitapp/pages/chat_with_ghost.py @@ -3,7 +3,7 @@ import streamlit_paste_button as spb import time from PIL.Image import Image -from typing import Iterable, List +from typing import Iterable, List, Optional from ghostos.prototypes.streamlitapp.pages.router import ( GhostChatRoute, GhostTaskRoute, ) @@ -25,8 +25,7 @@ ImageAssetMessage, ) from streamlit.logger import get_logger -from ghostos.framework.openai_realtime import RealtimeApp -from ghostos.abcd.realtime import OperatorName +from ghostos.abcd.realtime import OperatorName, RealtimeApp from ghostos.abcd import Shell, Conversation, Context from ghostos.identifier import get_identifier from ghostos.entity import to_entity_meta @@ -96,22 +95,27 @@ def main_chat(): realtime_app.close() realtime_app = get_realtime_app(conversation) created = True - Singleton(realtime_app, RealtimeApp).bind(st.session_state) - if created: - realtime_app.start(vad_mode=route.vad_mode, listen_mode=route.listen_mode) + # error about realtime. + if realtime_app is None: + st.error("Realtime mode need pyaudio installed successfully. Not ready yet") + route.realtime = False else: - realtime_app.set_mode(vad_mode=route.vad_mode, listen_mode=route.listen_mode) - - canceled = get_response_button_count() - if not route.vad_mode: - if st.button("response", key=f"create_realtime_response_{canceled}"): + Singleton(realtime_app, RealtimeApp).bind(st.session_state) + if created: + realtime_app.start(vad_mode=route.vad_mode, listen_mode=route.listen_mode) + else: + realtime_app.set_mode(vad_mode=route.vad_mode, listen_mode=route.listen_mode) + + canceled = get_response_button_count() + if not route.vad_mode: + if st.button("response", key=f"create_realtime_response_{canceled}"): + incr_response_button_count() + realtime_app.operate(OperatorName.respond.new("")) + st.rerun() + if st.button("cancel response", key=f"cancel_realtime_response_{canceled}"): incr_response_button_count() - realtime_app.operate(OperatorName.respond.new("")) + realtime_app.operate(OperatorName.cancel_responding.new("")) st.rerun() - if st.button("cancel response", key=f"cancel_realtime_response_{canceled}"): - incr_response_button_count() - realtime_app.operate(OperatorName.cancel_responding.new("")) - st.rerun() else: if realtime_app is not None: @@ -186,6 +190,8 @@ def main_chat(): def run_realtime(route: GhostChatRoute, app: RealtimeApp): + if app is None: + st.error("Realtime mode is not installed ready yet.") try: _run_realtime(route, app) except Exception as e: @@ -238,7 +244,12 @@ def _run_realtime(route: GhostChatRoute, app: RealtimeApp): st.write("app is close") -def get_realtime_app(conversation: Conversation) -> RealtimeApp: +def get_realtime_app(conversation: Conversation) -> Optional[RealtimeApp]: + try: + import pyaudio + except ImportError: + return None + from ghostos.framework.audio import get_pyaudio_pcm16_speaker, get_pyaudio_pcm16_listener from ghostos.framework.openai_realtime import get_openai_realtime_app speaker = get_pyaudio_pcm16_speaker() From aab38167627c1cb42f011d29be2079a96e634fe1 Mon Sep 17 00:00:00 2001 From: zhuming Date: Sun, 5 Jan 2025 15:42:31 +0800 Subject: [PATCH 3/4] dev: support developer message role and openai-o1-models --- ghostos/app/.example.env | 0 ghostos/app/.gitignore | 0 ghostos/app/.streamlit/config.toml | 0 .../docs/ghostos/en/aifunc_introduction.md | 0 .../docs/ghostos/zh/aifunc/introduction.md | 0 .../docs/ghostos/zh/aifunc/request_info.md | 0 .../docs/ghostos/zh/aifunc/usage_example.md | 0 ghostos/app/configs/documents_registry.yml | 0 ghostos/app/configs/ghostos_conf.yml | 0 ghostos/app/configs/llms_conf.yml | 72 ++++---- .../app/configs/openai_realtime_config.yml | 0 ghostos/app/configs/realtime_conf.yml | 0 ghostos/app/configs/registered_aifunc.yml | 0 ghostos/app/configs/streamlit_app.yml | 4 +- ghostos/app/memories/.gitkeep | 0 ghostos/app/runtime/aifunc_frames/.gitignore | 0 ghostos/app/runtime/audios/.gitignore | 0 ghostos/app/runtime/cache/.gitignore | 0 ghostos/app/runtime/events/.gitignore | 0 ghostos/app/runtime/images/.gitignore | 0 ghostos/app/runtime/logs/.gitignore | 0 ghostos/app/runtime/processes/.gitignore | 0 ghostos/app/runtime/prompts/.gitignore | 0 ghostos/app/runtime/tasks/.gitignore | 0 ghostos/app/runtime/threads/.gitignore | 0 ghostos/app/runtime/variables/.gitignore | 0 ghostos/app/source/.gitkeep | 0 ghostos/contracts/configs.py | 2 +- ghostos/core/llms/abcd.py | 33 +++- ghostos/core/llms/configs.py | 39 ++++- ghostos/core/messages/message.py | 1 + ghostos/core/messages/payload.py | 10 ++ ghostos/demo/agents/jojo.py | 1 + ghostos/demo/test_agents/__init__.py | 0 ghostos/demo/test_agents/deepseek_chat.py | 14 ++ ghostos/demo/test_agents/moonshot.py | 14 ++ ghostos/demo/test_agents/openai_o1_mini.py | 14 ++ ghostos/framework/llms/llms.py | 6 +- ghostos/framework/llms/openai_driver.py | 157 +++++++++++++++--- .../openai_realtime/event_data_objects.py | 2 +- ghostos/ghosts/chatbot/simplest.py | 5 +- ghostos/scripts/cli/run_streamlit_app.py | 1 - 42 files changed, 296 insertions(+), 79 deletions(-) mode change 100644 => 100755 ghostos/app/.example.env mode change 100644 => 100755 ghostos/app/.gitignore mode change 100644 => 100755 ghostos/app/.streamlit/config.toml mode change 100644 => 100755 ghostos/app/assets/docs/ghostos/en/aifunc_introduction.md mode change 100644 => 100755 ghostos/app/assets/docs/ghostos/zh/aifunc/introduction.md mode change 100644 => 100755 ghostos/app/assets/docs/ghostos/zh/aifunc/request_info.md mode change 100644 => 100755 ghostos/app/assets/docs/ghostos/zh/aifunc/usage_example.md mode change 100644 => 100755 ghostos/app/configs/documents_registry.yml mode change 100644 => 100755 ghostos/app/configs/ghostos_conf.yml mode change 100644 => 100755 ghostos/app/configs/llms_conf.yml mode change 100644 => 100755 ghostos/app/configs/openai_realtime_config.yml mode change 100644 => 100755 ghostos/app/configs/realtime_conf.yml mode change 100644 => 100755 ghostos/app/configs/registered_aifunc.yml mode change 100644 => 100755 ghostos/app/configs/streamlit_app.yml mode change 100644 => 100755 ghostos/app/memories/.gitkeep mode change 100644 => 100755 ghostos/app/runtime/aifunc_frames/.gitignore mode change 100644 => 100755 ghostos/app/runtime/audios/.gitignore mode change 100644 => 100755 ghostos/app/runtime/cache/.gitignore mode change 100644 => 100755 ghostos/app/runtime/events/.gitignore mode change 100644 => 100755 ghostos/app/runtime/images/.gitignore mode change 100644 => 100755 ghostos/app/runtime/logs/.gitignore mode change 100644 => 100755 ghostos/app/runtime/processes/.gitignore mode change 100644 => 100755 ghostos/app/runtime/prompts/.gitignore mode change 100644 => 100755 ghostos/app/runtime/tasks/.gitignore mode change 100644 => 100755 ghostos/app/runtime/threads/.gitignore mode change 100644 => 100755 ghostos/app/runtime/variables/.gitignore mode change 100644 => 100755 ghostos/app/source/.gitkeep create mode 100644 ghostos/demo/test_agents/__init__.py create mode 100644 ghostos/demo/test_agents/deepseek_chat.py create mode 100644 ghostos/demo/test_agents/moonshot.py create mode 100644 ghostos/demo/test_agents/openai_o1_mini.py diff --git a/ghostos/app/.example.env b/ghostos/app/.example.env old mode 100644 new mode 100755 diff --git a/ghostos/app/.gitignore b/ghostos/app/.gitignore old mode 100644 new mode 100755 diff --git a/ghostos/app/.streamlit/config.toml b/ghostos/app/.streamlit/config.toml old mode 100644 new mode 100755 diff --git a/ghostos/app/assets/docs/ghostos/en/aifunc_introduction.md b/ghostos/app/assets/docs/ghostos/en/aifunc_introduction.md old mode 100644 new mode 100755 diff --git a/ghostos/app/assets/docs/ghostos/zh/aifunc/introduction.md b/ghostos/app/assets/docs/ghostos/zh/aifunc/introduction.md old mode 100644 new mode 100755 diff --git a/ghostos/app/assets/docs/ghostos/zh/aifunc/request_info.md b/ghostos/app/assets/docs/ghostos/zh/aifunc/request_info.md old mode 100644 new mode 100755 diff --git a/ghostos/app/assets/docs/ghostos/zh/aifunc/usage_example.md b/ghostos/app/assets/docs/ghostos/zh/aifunc/usage_example.md old mode 100644 new mode 100755 diff --git a/ghostos/app/configs/documents_registry.yml b/ghostos/app/configs/documents_registry.yml old mode 100644 new mode 100755 diff --git a/ghostos/app/configs/ghostos_conf.yml b/ghostos/app/configs/ghostos_conf.yml old mode 100644 new mode 100755 diff --git a/ghostos/app/configs/llms_conf.yml b/ghostos/app/configs/llms_conf.yml old mode 100644 new mode 100755 index 8e18659c..0cc18260 --- a/ghostos/app/configs/llms_conf.yml +++ b/ghostos/app/configs/llms_conf.yml @@ -2,7 +2,7 @@ default: gpt-4o models: claude-3-5-sonnet: - kwargs: {} + kwargs: { } max_tokens: 2000 message_types: null model: claude-3-5-sonnet-20240620 @@ -13,7 +13,7 @@ models: timeout: 30 use_tools: true claude-3-haiku: - kwargs: {} + kwargs: { } max_tokens: 2000 message_types: null model: claude-3-haiku-20240307 @@ -24,10 +24,10 @@ models: timeout: 30 use_tools: true deepseek-chat: - kwargs: {} + kwargs: { } max_tokens: 2000 message_types: null - model: deepseek/deepseek-chat + model: deepseek-chat n: 1 request_timeout: 40 service: deepseek @@ -35,7 +35,7 @@ models: timeout: 30 use_tools: true deepseek-coder: - kwargs: {} + kwargs: { } max_tokens: 2000 message_types: null model: deepseek/deepseek-coder @@ -46,7 +46,7 @@ models: timeout: 30 use_tools: true gpt-3.5-turbo: - kwargs: {} + kwargs: { } max_tokens: 2000 message_types: null model: gpt-3.5-turbo @@ -57,7 +57,7 @@ models: timeout: 30 use_tools: true gpt-4: - kwargs: {} + kwargs: { } max_tokens: 2000 message_types: null model: gpt-4 @@ -68,7 +68,7 @@ models: timeout: 30 use_tools: true gpt-4-turbo: - kwargs: {} + kwargs: { } max_tokens: 2000 message_types: null model: gpt-4-turbo @@ -78,8 +78,16 @@ models: temperature: 0.7 timeout: 30 use_tools: true + gpt-o1-mini: + model: o1-mini + service: openai + reasoning: { } + gpt-o1: + model: o1 + service: openai + reasoning: { } gpt-4o: - kwargs: {} + kwargs: { } max_tokens: 2000 message_types: null model: gpt-4o @@ -90,7 +98,7 @@ models: timeout: 30 use_tools: true moonshot-v1-128k: - kwargs: {} + kwargs: { } max_tokens: 2000 message_types: null model: moonshot-v1-128k @@ -101,7 +109,7 @@ models: timeout: 30 use_tools: true moonshot-v1-32k: - kwargs: {} + kwargs: { } max_tokens: 2000 message_types: null model: moonshot-v1-32k @@ -112,7 +120,7 @@ models: timeout: 30 use_tools: true moonshot-v1-8k: - kwargs: {} + kwargs: { } max_tokens: 2000 message_types: null model: moonshot-v1-8k @@ -123,23 +131,23 @@ models: timeout: 30 use_tools: true services: -- base_url: https://api.moonshot.cn/v1 - driver: openai_driver - name: moonshot - proxy: null - token: $MOONSHOT_API_KEY -- base_url: https://api.openai.com/v1 - driver: openai_driver - name: openai - proxy: $OPENAI_PROXY - token: $OPENAI_API_KEY -- base_url: https://api.anthropic.com/v1 - driver: openai_driver - name: anthropic - proxy: $ANTHROPIC_PROXY - token: $ANTHROPIC_API_KEY -- base_url: https://api.deepseek.com/beta - driver: openai_driver - name: deepseek - proxy: null - token: $DEEPSEEK_API_KEY + - base_url: https://api.moonshot.cn/v1 + driver: openai_driver + name: moonshot + token: $MOONSHOT_API_KEY + - base_url: https://api.openai.com/v1 + driver: openai_driver + name: openai + proxy: $OPENAI_PROXY + token: $OPENAI_API_KEY + compatible: + use_developer_role: true + - base_url: https://api.anthropic.com/v1 + driver: lite_llm_driver + name: anthropic + proxy: $ANTHROPIC_PROXY + token: $ANTHROPIC_API_KEY + - base_url: https://api.deepseek.com/beta + driver: openai_driver + name: deepseek + token: $DEEPSEEK_API_KEY diff --git a/ghostos/app/configs/openai_realtime_config.yml b/ghostos/app/configs/openai_realtime_config.yml old mode 100644 new mode 100755 diff --git a/ghostos/app/configs/realtime_conf.yml b/ghostos/app/configs/realtime_conf.yml old mode 100644 new mode 100755 diff --git a/ghostos/app/configs/registered_aifunc.yml b/ghostos/app/configs/registered_aifunc.yml old mode 100644 new mode 100755 diff --git a/ghostos/app/configs/streamlit_app.yml b/ghostos/app/configs/streamlit_app.yml old mode 100644 new mode 100755 index ba7b6af7..249f9e14 --- a/ghostos/app/configs/streamlit_app.yml +++ b/ghostos/app/configs/streamlit_app.yml @@ -1,6 +1,4 @@ # from class: ghostos.prototypes.streamlitapp.resources:AppConf bool_options: - DEBUG_MODE: false + DEBUG_MODE: true HELP_MODE: false -domain: ghostos -lang: zh diff --git a/ghostos/app/memories/.gitkeep b/ghostos/app/memories/.gitkeep old mode 100644 new mode 100755 diff --git a/ghostos/app/runtime/aifunc_frames/.gitignore b/ghostos/app/runtime/aifunc_frames/.gitignore old mode 100644 new mode 100755 diff --git a/ghostos/app/runtime/audios/.gitignore b/ghostos/app/runtime/audios/.gitignore old mode 100644 new mode 100755 diff --git a/ghostos/app/runtime/cache/.gitignore b/ghostos/app/runtime/cache/.gitignore old mode 100644 new mode 100755 diff --git a/ghostos/app/runtime/events/.gitignore b/ghostos/app/runtime/events/.gitignore old mode 100644 new mode 100755 diff --git a/ghostos/app/runtime/images/.gitignore b/ghostos/app/runtime/images/.gitignore old mode 100644 new mode 100755 diff --git a/ghostos/app/runtime/logs/.gitignore b/ghostos/app/runtime/logs/.gitignore old mode 100644 new mode 100755 diff --git a/ghostos/app/runtime/processes/.gitignore b/ghostos/app/runtime/processes/.gitignore old mode 100644 new mode 100755 diff --git a/ghostos/app/runtime/prompts/.gitignore b/ghostos/app/runtime/prompts/.gitignore old mode 100644 new mode 100755 diff --git a/ghostos/app/runtime/tasks/.gitignore b/ghostos/app/runtime/tasks/.gitignore old mode 100644 new mode 100755 diff --git a/ghostos/app/runtime/threads/.gitignore b/ghostos/app/runtime/threads/.gitignore old mode 100644 new mode 100755 diff --git a/ghostos/app/runtime/variables/.gitignore b/ghostos/app/runtime/variables/.gitignore old mode 100644 new mode 100755 diff --git a/ghostos/app/source/.gitkeep b/ghostos/app/source/.gitkeep old mode 100644 new mode 100755 diff --git a/ghostos/contracts/configs.py b/ghostos/contracts/configs.py index 7ca79f58..6203ac66 100644 --- a/ghostos/contracts/configs.py +++ b/ghostos/contracts/configs.py @@ -102,7 +102,7 @@ def unmarshal(cls, content: str) -> "Config": return cls(**value) def marshal(self) -> bytes: - value = self.model_dump(exclude_defaults=False) + value = self.model_dump(exclude_defaults=True) comment = f"# from class: {generate_import_path(self.__class__)}" result = yaml.safe_dump(value) return "\n".join([comment, result]).encode() diff --git a/ghostos/core/llms/abcd.py b/ghostos/core/llms/abcd.py index e6cb1a89..f9ae7565 100644 --- a/ghostos/core/llms/abcd.py +++ b/ghostos/core/llms/abcd.py @@ -16,6 +16,14 @@ class LLMApi(ABC): uniform interface for large language models in GhostOS. """ + service: ServiceConf + model: ModelConf + + @property + @abstractmethod + def name(self) -> str: + pass + @abstractmethod def get_service(self) -> ServiceConf: """ @@ -46,25 +54,32 @@ def text_completion(self, prompt: str) -> str: pass @abstractmethod - def chat_completion(self, chat: Prompt) -> Message: + def chat_completion(self, prompt: Prompt) -> Message: pass @abstractmethod - def chat_completion_chunks(self, chat: Prompt) -> Iterable[Message]: + def chat_completion_chunks(self, prompt: Prompt) -> Iterable[Message]: """ todo: 暂时先这么定义了. """ pass - def deliver_chat_completion(self, chat: Prompt, stream: bool) -> Iterable[Message]: + @abstractmethod + def reasoning_completion(self, prompt: Prompt, stream: bool) -> Iterable[Message]: + pass + + def deliver_chat_completion(self, prompt: Prompt, stream: bool) -> Iterable[Message]: """ 逐个发送消息的包. """ - if not stream: - message = self.chat_completion(chat) - return [message] + if self.model.reasoning is not None: + yield from self.reasoning_completion(prompt, stream) - yield from self.chat_completion_chunks(chat) + elif not stream or not self.model.allow_streaming: + message = self.chat_completion(prompt) + return [message] + else: + yield from self.chat_completion_chunks(prompt) class LLMDriver(ABC): @@ -78,7 +93,7 @@ def driver_name(self) -> str: pass @abstractmethod - def new(self, service: ServiceConf, model: ModelConf) -> LLMApi: + def new(self, service: ServiceConf, model: ModelConf, api_name: str = "") -> LLMApi: pass @@ -130,7 +145,7 @@ def each_api_conf(self) -> Iterable[Tuple[ServiceConf, ModelConf]]: pass @abstractmethod - def new_api(self, service_conf: ServiceConf, api_conf: ModelConf) -> LLMApi: + def new_api(self, service_conf: ServiceConf, api_conf: ModelConf, api_name: str = "") -> LLMApi: """ instance a LLMApi by configs. """ diff --git a/ghostos/core/llms/configs.py b/ghostos/core/llms/configs.py index 57f23caa..a56548e9 100644 --- a/ghostos/core/llms/configs.py +++ b/ghostos/core/llms/configs.py @@ -2,7 +2,7 @@ import os -from typing import List, Dict, Optional, Any, ClassVar +from typing import List, Dict, Optional, Any, ClassVar, Literal from pydantic import BaseModel, Field from ghostos.core.messages import Payload from ghostos.helpers import gettext as _ @@ -15,7 +15,17 @@ OPENAI_DRIVER_NAME = "openai_driver" """default llm driver name for OpenAI llm message protocol """ -LITELLM_DRIVER_NAME = "lite_llm_Driver" +LITELLM_DRIVER_NAME = "lite_llm_driver" + + +class Reasonable(BaseModel): + """ + the OpenAI reasoning configs adapter + """ + effort: Literal["low", "medium", "high"] = Field( + "medium", + description="reasoning effort level", + ) class ModelConf(Payload): @@ -34,7 +44,27 @@ class ModelConf(Payload): request_timeout: float = Field(default=40, description="request timeout") kwargs: Dict[str, Any] = Field(default_factory=dict, description="kwargs") use_tools: bool = Field(default=True, description="use tools") + max_completion_tokens: Optional[int] = Field( + None, + description="max completion tokens", + ) message_types: Optional[List[str]] = Field(None, description="model allow message types") + allow_streaming: bool = Field(True, description="if the current model allow streaming") + reasoning: Optional[Reasonable] = Field( + default=None, + description="reasoning configuration", + ) + + payloads: Dict[str, Dict] = Field( + default_factory=dict, + description="custom payload objects. save strong typed but optional dict." + "see ghostos.core.messages.Payload class." + ) + + +class Compatible(BaseModel): + use_developer_role: bool = Field(default=False, description="use developer role instead of system") + allow_system_in_messages: bool = Field(default=True, description="allow system messages in history") class ServiceConf(BaseModel): @@ -55,6 +85,11 @@ class ServiceConf(BaseModel): description="the adapter driver name of this service. change it only if you know what you are doing", ) + compatible: Compatible = Field( + default_factory=Compatible, + description="the model api compatible configuration", + ) + def load(self, environ: Optional[Dict] = None) -> None: self.token = self._load_env(self.token, environ=environ) if self.proxy is not None: diff --git a/ghostos/core/messages/message.py b/ghostos/core/messages/message.py index 826487ca..093b776a 100644 --- a/ghostos/core/messages/message.py +++ b/ghostos/core/messages/message.py @@ -30,6 +30,7 @@ class Role(str, enum.Enum): USER = "user" ASSISTANT = "assistant" SYSTEM = "system" + DEVELOPER = "developer" @classmethod def all(cls) -> Set[str]: diff --git a/ghostos/core/messages/payload.py b/ghostos/core/messages/payload.py index cd718476..934fdde5 100644 --- a/ghostos/core/messages/payload.py +++ b/ghostos/core/messages/payload.py @@ -25,6 +25,16 @@ def read_payload(cls, message: Union[Message, HasPayloads]) -> Optional[Self]: return None return cls(**value) + @classmethod + def read_payload_default(cls, message: Union[Message, HasPayloads]) -> Self: + """ + only for payload class that has default values + """ + r = cls.read_payload(message) + if r is None: + r = cls() + return r + def set_payload(self, message: Union[Message, HasPayloads]) -> None: message.payloads[self.key] = self.model_dump() diff --git a/ghostos/demo/agents/jojo.py b/ghostos/demo/agents/jojo.py index 458d3b30..073a1937 100644 --- a/ghostos/demo/agents/jojo.py +++ b/ghostos/demo/agents/jojo.py @@ -4,6 +4,7 @@ # so the script `ghostos web` or `ghostos console` can detect it # and run agent application with this ghost. __ghost__ = Chatbot( + id=__name__, name="jojo", description="a chatbot for baseline test", persona="you are an LLM-driven cute girl, named jojo", diff --git a/ghostos/demo/test_agents/__init__.py b/ghostos/demo/test_agents/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ghostos/demo/test_agents/deepseek_chat.py b/ghostos/demo/test_agents/deepseek_chat.py new file mode 100644 index 00000000..e88d5d42 --- /dev/null +++ b/ghostos/demo/test_agents/deepseek_chat.py @@ -0,0 +1,14 @@ +from ghostos.ghosts.chatbot import Chatbot + +# the __ghost__ magic attr define a ghost instance +# so the script `ghostos web` or `ghostos console` can detect it +# and run agent application with this ghost. +__ghost__ = Chatbot( + id=__name__, + name="jojo", + description="a chatbot for baseline test", + persona="you are an LLM-driven cute girl, named jojo", + instruction="remember talk to user with user's language.", + # set model using openai-o1 + llm_api="deepseek-chat", +) diff --git a/ghostos/demo/test_agents/moonshot.py b/ghostos/demo/test_agents/moonshot.py new file mode 100644 index 00000000..f8bade4e --- /dev/null +++ b/ghostos/demo/test_agents/moonshot.py @@ -0,0 +1,14 @@ +from ghostos.ghosts.chatbot import Chatbot + +# the __ghost__ magic attr define a ghost instance +# so the script `ghostos web` or `ghostos console` can detect it +# and run agent application with this ghost. +__ghost__ = Chatbot( + id=__name__, + name="jojo", + description="a chatbot for baseline test", + persona="you are an LLM-driven cute girl, named jojo", + instruction="remember talk to user with user's language.", + # set model using openai-o1 + llm_api="moonshot-v1-32k", +) diff --git a/ghostos/demo/test_agents/openai_o1_mini.py b/ghostos/demo/test_agents/openai_o1_mini.py new file mode 100644 index 00000000..821b02db --- /dev/null +++ b/ghostos/demo/test_agents/openai_o1_mini.py @@ -0,0 +1,14 @@ +from ghostos.ghosts.chatbot import Chatbot + +# the __ghost__ magic attr define a ghost instance +# so the script `ghostos web` or `ghostos console` can detect it +# and run agent application with this ghost. +__ghost__ = Chatbot( + id=__name__, + name="jojo", + description="a chatbot for baseline test", + persona="you are an LLM-driven cute girl, named jojo", + instruction="remember talk to user with user's language.", + # set model using openai-o1 + llm_api="gpt-o1-mini", +) diff --git a/ghostos/framework/llms/llms.py b/ghostos/framework/llms/llms.py index 05b26962..a5347f53 100644 --- a/ghostos/framework/llms/llms.py +++ b/ghostos/framework/llms/llms.py @@ -67,9 +67,9 @@ def each_api_conf(self) -> Iterator[Tuple[ServiceConf, ModelConf]]: continue yield service, model_conf - def new_api(self, service_conf: ServiceConf, api_conf: ModelConf) -> LLMApi: + def new_api(self, service_conf: ServiceConf, api_conf: ModelConf, api_name: str = "") -> LLMApi: driver = self._llm_drivers.get(service_conf.driver, self._default_driver) - return driver.new(service_conf, api_conf) + return driver.new(service_conf, api_conf, api_name=api_name) def get_api(self, api_name: str) -> Optional[LLMApi]: api = self._apis.get(api_name, None) @@ -85,7 +85,7 @@ def get_api(self, api_name: str) -> Optional[LLMApi]: service_conf = self._llm_services.get(model_conf.service, None) if service_conf is None: return None - api = self.new_api(service_conf, model_conf) + api = self.new_api(service_conf, model_conf, api_name=api_name) # 解决缓存问题. 工业场景中, 或许不该缓存, 因为配置随时可能变化. if api is not None: self._apis[api_name] = api diff --git a/ghostos/framework/llms/openai_driver.py b/ghostos/framework/llms/openai_driver.py index ada8d4ea..9a0f0ed8 100644 --- a/ghostos/framework/llms/openai_driver.py +++ b/ghostos/framework/llms/openai_driver.py @@ -3,7 +3,7 @@ from httpx import Client from httpx_socks import SyncProxyTransport from openai import NOT_GIVEN -from openai.types.chat import ChatCompletion +from openai.types.chat import ChatCompletion, ChatCompletionReasoningEffort from openai.types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam from openai.types.chat.chat_completion_message_param import ChatCompletionMessageParam from openai.types.chat.chat_completion_chunk import ChatCompletionChunk @@ -78,9 +78,11 @@ def __init__( logger: LoggerItf, # deprecated: functional_token_prompt: Optional[str] = None, + api_name: str = "", ): - self._service = service_conf - self._model = model_conf + self._api_name = api_name + self.service = service_conf.model_copy(deep=True) + self.model = model_conf.model_copy(deep=True) self._storage: PromptStorage = storage self._logger = logger http_client = None @@ -99,17 +101,51 @@ def __init__( functional_token_prompt = DEFAULT_FUNCTIONAL_TOKEN_PROMPT self._functional_token_prompt = functional_token_prompt + @property + def name(self) -> str: + return self._api_name + def get_service(self) -> ServiceConf: - return self._service + return self.service def get_model(self) -> ModelConf: - return self._model + return self.model def text_completion(self, prompt: str) -> str: raise NotImplemented("text_completion is deprecated, implement it later") def parse_message_params(self, messages: List[Message]) -> List[ChatCompletionMessageParam]: - return list(self._parser.parse_message_list(messages, self._model.message_types)) + messages = self.parse_by_compatible_settings(messages) + return list(self._parser.parse_message_list(messages, self.model.message_types)) + + def parse_by_compatible_settings(self, messages: List[Message]) -> List[Message]: + # developer role test + if self.service.compatible.use_developer_role: + changed = [] + for message in messages: + if message.role == Role.SYSTEM: + message = message.model_copy(update={"role": Role.DEVELOPER}, deep=True) + changed.append(message) + messages = changed + else: + changed = [] + for message in messages: + if message.role == Role.DEVELOPER: + message = message.model_copy(update={"role": Role.SYSTEM}, deep=True) + changed.append(message) + messages = changed + + # allow system messages + if not self.service.compatible.allow_system_in_messages: + changed = [] + for message in messages: + if message.role == Role.SYSTEM or message.role == Role.DEVELOPER: + name = f"__{message.role}__" + message = message.model_copy(update={"role": Role.USER, "name": name}, deep=True) + changed.append(message) + messages = changed + + return messages def _chat_completion(self, prompt: Prompt, stream: bool) -> Union[ChatCompletion, Iterable[ChatCompletionChunk]]: prompt = self.parse_prompt(prompt) @@ -124,23 +160,23 @@ def _chat_completion(self, prompt: Prompt, stream: bool) -> Union[ChatCompletion self._logger.debug(f"start chat completion messages %s", messages) functions = prompt.get_openai_functions() tools = prompt.get_openai_tools() - if self._model.use_tools: + if self.model.use_tools: functions = NOT_GIVEN else: tools = NOT_GIVEN return self._client.chat.completions.create( messages=messages, - model=self._model.model, + model=self.model.model, function_call=prompt.get_openai_function_call(), functions=functions, tools=tools, - max_tokens=self._model.max_tokens, - temperature=self._model.temperature, - n=self._model.n, - timeout=self._model.timeout, + max_tokens=self.model.max_tokens, + temperature=self.model.temperature, + n=self.model.n, + timeout=self.model.timeout, stream=stream, stream_options=include_usage, - **self._model.kwargs, + **self.model.kwargs, ) except Exception as e: self._logger.error(f"error chat completion for prompt {prompt.id}: {e}") @@ -155,7 +191,7 @@ def chat_completion(self, prompt: Prompt) -> Message: prompt.added = [message] pack = self._parser.from_chat_completion(message.choices[0].message) # add completion usage - self._model.set_payload(pack) + self.model.set_payload(pack) if message.usage: usage = CompletionUsagePayload.from_usage(message.usage) usage.set_payload(pack) @@ -169,6 +205,77 @@ def chat_completion(self, prompt: Prompt) -> Message: finally: self._storage.save(prompt) + def reasoning_completion(self, prompt: Prompt, stream: bool) -> Iterable[Message]: + try: + message: ChatCompletion = self._reasoning_completion(prompt) + prompt.added = [message] + pack = self._parser.from_chat_completion(message.choices[0].message) + # add completion usage + self.model.set_payload(pack) + if message.usage: + usage = CompletionUsagePayload.from_usage(message.usage) + usage.set_payload(pack) + + if not pack.is_complete(): + pack.chunk = False + return [pack] + except Exception as e: + prompt.error = str(e) + raise + finally: + self._storage.save(prompt) + + def _reasoning_completion(self, prompt: Prompt) -> ChatCompletion: + if self.model.reasoning is None: + raise NotImplementedError(f"current model {self.model} does not support reasoning completion ") + + prompt = self.parse_prompt(prompt) + self._logger.info( + "start reasoning completion for prompt %s, model %s, reasoning conf %s", + prompt.id, + self.model.model_dump(exclude_defaults=True), + self.model.reasoning, + ) + # include_usage = ChatCompletionStreamOptionsParam(include_usage=True) if stream else NOT_GIVEN + messages = prompt.get_messages() + messages = self.parse_message_params(messages) + if not messages: + raise AttributeError("empty chat!!") + try: + prompt.run_start = timestamp() + self._logger.debug(f"start reasoning completion messages %s", messages) + functions = prompt.get_openai_functions() + tools = prompt.get_openai_tools() + if self.model.use_tools: + functions = NOT_GIVEN + else: + tools = NOT_GIVEN + if self.model.reasoning.effort is None: + reasoning_effort = NOT_GIVEN + else: + reasoning_effort = self.model.reasoning.effort + + return self._client.chat.completions.create( + messages=messages, + model=self.model.model, + # add this parameters then failed: + # Error code: 400 - {'error': {'message': "Unknown parameter: 'reasoning_effort'.", + # 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': 'unknown_parameter'} + # reasoning_effort=reasoning_effort, + function_call=prompt.get_openai_function_call(), + functions=functions, + tools=tools, + n=self.model.n, + timeout=self.model.timeout, + **self.model.kwargs, + ) + except Exception as e: + self._logger.error(f"error reasoning completion for prompt {prompt.id}: {e}") + raise + finally: + self._logger.debug(f"end reasoning completion for prompt {prompt.id}") + prompt.run_end = timestamp() + def chat_completion_chunks(self, prompt: Prompt) -> Iterable[Message]: try: chunks: Iterable[ChatCompletionChunk] = self._chat_completion(prompt, stream=True) @@ -178,7 +285,7 @@ def chat_completion_chunks(self, prompt: Prompt) -> Iterable[Message]: for chunk in messages: yield chunk if chunk.is_complete(): - self._model.set_payload(chunk) + self.model.set_payload(chunk) prompt_payload.set_payload(chunk) output.append(chunk) prompt.added = output @@ -189,7 +296,7 @@ def chat_completion_chunks(self, prompt: Prompt) -> Iterable[Message]: self._storage.save(prompt) def parse_prompt(self, prompt: Prompt) -> Prompt: - prompt.model = self._model + prompt.model = self.model return prompt @@ -208,9 +315,9 @@ def __init__(self, storage: PromptStorage, logger: LoggerItf, parser: Optional[O def driver_name(self) -> str: return OPENAI_DRIVER_NAME - def new(self, service: ServiceConf, model: ModelConf) -> LLMApi: + def new(self, service: ServiceConf, model: ModelConf, api_name: str = "") -> LLMApi: get_ghostos_logger().debug(f"new llm api %s at service %s", model.model, service.name) - return OpenAIAdapter(service, model, self._parser, self._storage, self._logger) + return OpenAIAdapter(service, model, self._parser, self._storage, self._logger, api_name=api_name) class LitellmAdapter(OpenAIAdapter): @@ -223,14 +330,14 @@ def _chat_completion(self, chat: Prompt, stream: bool) -> ChatCompletion: messages = chat.get_messages() messages = self.parse_message_params(messages) response = litellm.completion( - model=self._model.model, + model=self.model.model, messages=list(messages), - timeout=self._model.timeout, - temperature=self._model.temperature, - n=self._model.n, + timeout=self.model.timeout, + temperature=self.model.temperature, + n=self.model.n, # not support stream yet stream=False, - api_key=self._service.token, + api_key=self.service.token, ) return response.choices[0].message @@ -253,5 +360,5 @@ class LiteLLMDriver(OpenAIDriver): def driver_name(self) -> str: return LITELLM_DRIVER_NAME - def new(self, service: ServiceConf, model: ModelConf) -> LLMApi: - return LitellmAdapter(service, model, self._parser, self._storage, self._logger) + def new(self, service: ServiceConf, model: ModelConf, api_name: str = "") -> LLMApi: + return LitellmAdapter(service, model, self._parser, self._storage, self._logger, api_name=api_name) diff --git a/ghostos/framework/openai_realtime/event_data_objects.py b/ghostos/framework/openai_realtime/event_data_objects.py index d3bf8666..d65e6e28 100644 --- a/ghostos/framework/openai_realtime/event_data_objects.py +++ b/ghostos/framework/openai_realtime/event_data_objects.py @@ -319,7 +319,7 @@ class SessionObjectBase(BaseModel): """ model: OpenAIRealtimeModel = Field(OpenAIRealtimeModel.gpt_4o_realtime_preview_2024_12_17) modalities: List[str] = Field(default_factory=lambda: ["audio", "text"], enum={"text", "audio"}) - voice: Voice = Field( + voice: str = Field( default="coral", description="Voice to use", ) diff --git a/ghostos/ghosts/chatbot/simplest.py b/ghostos/ghosts/chatbot/simplest.py index 095d9e2d..1ed83fa6 100644 --- a/ghostos/ghosts/chatbot/simplest.py +++ b/ghostos/ghosts/chatbot/simplest.py @@ -1,4 +1,4 @@ -from typing import Union, Iterable, ClassVar, List +from typing import Union, Iterable, ClassVar, List, Optional from ghostos.abcd import Agent, GhostDriver, Session, Operator from ghostos.abcd.thoughts import LLMThought, Thought @@ -22,6 +22,7 @@ class Chatbot(ModelEntity, Agent): instruction: str = Field(description="instruction of the chatbot") llm_api: str = Field(default="", description="llm api of the chatbot") history_turns: int = Field(default=20, description="history turns of thread max turns") + id: Optional[str] = Field(default=None) ArtifactType: ClassVar = None ContextType: ClassVar = None @@ -29,7 +30,7 @@ class Chatbot(ModelEntity, Agent): def __identifier__(self) -> Identifier: return Identifier( - id=None, + id=self.id, name=self.name, description=self.description, ) diff --git a/ghostos/scripts/cli/run_streamlit_app.py b/ghostos/scripts/cli/run_streamlit_app.py index 633a8f5d..203b6569 100644 --- a/ghostos/scripts/cli/run_streamlit_app.py +++ b/ghostos/scripts/cli/run_streamlit_app.py @@ -10,7 +10,6 @@ from os import path __all__ = [ - "run_streamlit_app.py", "start_streamlit_prototype_cli", "RunGhostChatApp", "get_config_flag_options", From 74befb6445366a6fe793701523ec60b1341652dd Mon Sep 17 00:00:00 2001 From: zhuming Date: Sun, 5 Jan 2025 16:30:06 +0800 Subject: [PATCH 4/4] dev: prepare v0.1.0 commit --- .github/workflows/python-publish.yml | 70 ------------------- README.md | 33 ++++++++- docs/en/README.md | 33 ++++++++- docs/en/getting_started/installation.md | 11 ++- docs/zh-cn/README.md | 32 ++++++++- docs/zh-cn/getting_started/installation.md | 9 +++ ghostos/app/configs/streamlit_app.yml | 2 +- .../streamlitapp/pages/chat_with_ghost.py | 5 +- pyproject.toml | 13 ++-- 9 files changed, 127 insertions(+), 81 deletions(-) delete mode 100644 .github/workflows/python-publish.yml diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml deleted file mode 100644 index 82f8dbd9..00000000 --- a/.github/workflows/python-publish.yml +++ /dev/null @@ -1,70 +0,0 @@ -# This workflow will upload a Python Package to PyPI when a release is created -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries - -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -name: Upload Python Package - -on: - release: - types: [published] - -permissions: - contents: read - -jobs: - release-build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: "3.x" - - - name: Build release distributions - run: | - # NOTE: put your own distribution build steps here. - python -m pip install build - python -m build - - - name: Upload distributions - uses: actions/upload-artifact@v4 - with: - name: release-dists - path: dist/ - - pypi-publish: - runs-on: ubuntu-latest - needs: - - release-build - permissions: - # IMPORTANT: this permission is mandatory for trusted publishing - id-token: write - - # Dedicated environments with protections for publishing are strongly recommended. - # For more information, see: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#deployment-protection-rules - environment: - name: pypi - # OPTIONAL: uncomment and update to include your PyPI project URL in the deployment status: - # url: https://pypi.org/p/YOURPROJECT - # - # ALTERNATIVE: if your GitHub Release name is the PyPI project version string - # ALTERNATIVE: exactly, uncomment the following line instead: - # url: https://pypi.org/project/YOURPROJECT/${{ github.event.release.name }} - - steps: - - name: Retrieve release distributions - uses: actions/download-artifact@v4 - with: - name: release-dists - path: dist/ - - - name: Publish release distributions to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - packages-dir: dist/ diff --git a/README.md b/README.md index 9d45a9a5..c1838352 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,16 @@ ghostos init ``` Configure the model. Default to use OpenAI `gpt-4o`, requiring the environment variable `OPENAI_API_KEY`. -Or you can use configuration ui by streamlit: +```bash +export OPENAI_API_KEY="your openai api key" +# Optionals: +export OPENAI_PROXY="sock5://localhost:[your-port]" # setup openai proxy +export DEEPSEEK_API_KEY="your deepseek api key" +epoxrt MOONSHOT_API_KEY="your moonshot api key" +``` + +Or you can use configuration ui by streamlit: ```bash ghostos config ``` @@ -85,11 +93,32 @@ that can be instructed to call functions or methods within the file through natu ghostos web [my_path_file_path] ``` -ou can create a local Python file and define your own Agents. For more details +some demo agents +```bash +ghostos web ghostos.demo.agents.jojo +ghostos web ghostos.demo.test_agents.moonshot # moonshot-v1-32k model +ghostos web ghostos.demo.test_agents.deepseek_chat # deepseek chat model +ghostos web ghostos.demo.test_agents.openai_o1_mini # openai o1 mini model +``` + +You can create a local Python file and define your own Agents. For more details * [Chatbot](docs/zh-cn/usages/chatbot.md): simplest chatbot * [MossAgent](docs/zh-cn/usages/moss_agent.md): an agent that can interact with the python module +## Install Realtime + +`GhostOS` support [OpenAI Realtime](https://platform.openai.com/docs/guides/realtime), +using [pyaudio](https://pypi.org/project/PyAudio/) to handle realtime audio i/o. +Need to install the dependencies first: + +```bash +pip install 'ghostos[realtime]' +``` + +> You may face some difficulties while install pyaudio on your device, +> I'm sure gpt-4o, google or stackoverflow will offer you solutions. + ## Use In Python ```python diff --git a/docs/en/README.md b/docs/en/README.md index 07443a5d..7751c12f 100644 --- a/docs/en/README.md +++ b/docs/en/README.md @@ -66,6 +66,15 @@ ghostos init ``` Configure the model. Default to use OpenAI `gpt-4o`, requiring the environment variable `OPENAI_API_KEY`. + +```bash +export OPENAI_API_KEY="your openai api key" +# Optionals: +export OPENAI_PROXY="sock5://localhost:[your-port]" # setup openai proxy +export DEEPSEEK_API_KEY="your deepseek api key" +epoxrt MOONSHOT_API_KEY="your moonshot api key" +``` + Or you can use configuration ui by streamlit: ```bash @@ -86,11 +95,33 @@ that can be instructed to call functions or methods within the file through natu ghostos web [my_path_file_path] ``` -you can create a local Python file and define your own Agents. For more details +some demo agents + +```bash +ghostos web ghostos.demo.agents.jojo +ghostos web ghostos.demo.test_agents.moonshot # moonshot-v1-32k model +ghostos web ghostos.demo.test_agents.deepseek_chat # deepseek chat model +ghostos web ghostos.demo.test_agents.openai_o1_mini # openai o1 mini model +``` + +You can create a local Python file and define your own Agents. For more details * [Chatbot](/en/usages/chatbot.md): simplest chatbot * [MossAgent](/en/usages/moss_agent.md): an agent that can interact with the python module +## Install Realtime + +`GhostOS` support [OpenAI Realtime](https://platform.openai.com/docs/guides/realtime), +using [pyaudio](https://pypi.org/project/PyAudio/) to handle realtime audio i/o. +Need to install the dependencies first: + +```bash +pip install 'ghostos[realtime]' +``` + +> You may face some difficulties while install pyaudio on your device, +> I'm sure gpt-4o, google or stackoverflow will offer you solutions. + ## Use In Python ```python diff --git a/docs/en/getting_started/installation.md b/docs/en/getting_started/installation.md index 33d1293b..1edb9335 100644 --- a/docs/en/getting_started/installation.md +++ b/docs/en/getting_started/installation.md @@ -37,6 +37,14 @@ natural language dialogue. ghostos web [my_path_file_path] ``` +## Install Realtime + +install realtime dependencies (mainly `pyaudio`): + +```bash +pip install 'ghostos[realtime]' +``` + ## Workspace `GhostOS` is currently using local files to store runtime data, so it's necessary to initialize a workspace. @@ -56,7 +64,8 @@ ghostos clear-runtime There are two ways to define these environment variables: -Using a `.env` file (automatically read through `dotenv`) +* Export the environment variables to your shell. +* Using a `.env` file (automatically read through `dotenv`) ```bash diff --git a/docs/zh-cn/README.md b/docs/zh-cn/README.md index d3a00c1c..e2c7997b 100644 --- a/docs/zh-cn/README.md +++ b/docs/zh-cn/README.md @@ -59,7 +59,16 @@ ghostos init ``` 配置大模型. 默认使用 OpenAI `gpt-4o`, 要求环境变量存在 `OPENAI_API_KEY`. -或者运行 `streamlit` 编辑界面: + +```bash +export OPENAI_API_KEY="your openai api key" +# Optionals: +export OPENAI_PROXY="sock5://localhost:[your-port]" # setup openai proxy +export DEEPSEEK_API_KEY="your deepseek api key" +epoxrt MOONSHOT_API_KEY="your moonshot api key" +``` + +或者运行 `streamlit` 打开配置界面: ```bash ghostos config @@ -78,11 +87,32 @@ ghostos web ghostos.demo.agents.jojo ghostos web [my_path_file_path] ``` +当前的测试用例: + +```bash +ghostos web ghostos.demo.agents.jojo +ghostos web ghostos.demo.test_agents.moonshot # moonshot-v1-32k model +ghostos web ghostos.demo.test_agents.deepseek_chat # deepseek chat model +ghostos web ghostos.demo.test_agents.openai_o1_mini # openai o1 mini model +``` + 可以通过创建本地 python 文件, 定义出自己的 Agents. 详情请见: * [Chatbot](/zh-cn/usages/chatbot.md): 极简的对话机器人 * [MossAgent](/zh-cn/usages/moss_agent.md): 能使用 python 的 agent +## Install Realtime + +`GhostOS` 当前支持 [OpenAI Realtime](https://platform.openai.com/docs/guides/realtime), +使用 [pyaudio](https://pypi.org/project/PyAudio/) 来处理实时语音的输入输出. +需要安装相关依赖: + +```bash +pip install 'ghostos[realtime]' +``` + +> 在安装 pyaudio 的时候可能会遇到一些问题, 我相信 gpt-4o, google 和 stackoverflow 能够很好地帮助你解决它们. + ## Use In Python ```python diff --git a/docs/zh-cn/getting_started/installation.md b/docs/zh-cn/getting_started/installation.md index 17cfbd42..94d916fb 100644 --- a/docs/zh-cn/getting_started/installation.md +++ b/docs/zh-cn/getting_started/installation.md @@ -34,6 +34,14 @@ ghostos web ghostos.demo.agents.jojo ghostos web [my_path_file_path] ``` +## Install Realtime + +安装 realtime 所需的依赖: + +```bash +pip install 'ghostos[realtime]' +``` + ## Workspace `GhostOS` 当前版本使用本地文件来存运行时数据. 所以需要初始化一个 workspace. @@ -51,6 +59,7 @@ ghostos clear-runtime `GhostOS` 依赖各种模型的 `access token`, 默认是从环境变量中读取. 定义这些环境变量有两种方法: +- export 环境变量到命令行中. - 使用 `.env` 文件 (自动通过 `dotenv` 读取) ```bash diff --git a/ghostos/app/configs/streamlit_app.yml b/ghostos/app/configs/streamlit_app.yml index 249f9e14..e26d5379 100755 --- a/ghostos/app/configs/streamlit_app.yml +++ b/ghostos/app/configs/streamlit_app.yml @@ -1,4 +1,4 @@ # from class: ghostos.prototypes.streamlitapp.resources:AppConf bool_options: - DEBUG_MODE: true + DEBUG_MODE: false HELP_MODE: false diff --git a/ghostos/prototypes/streamlitapp/pages/chat_with_ghost.py b/ghostos/prototypes/streamlitapp/pages/chat_with_ghost.py index 02695fc0..cd5fc07e 100644 --- a/ghostos/prototypes/streamlitapp/pages/chat_with_ghost.py +++ b/ghostos/prototypes/streamlitapp/pages/chat_with_ghost.py @@ -97,7 +97,10 @@ def main_chat(): created = True # error about realtime. if realtime_app is None: - st.error("Realtime mode need pyaudio installed successfully. Not ready yet") + st.error( + "Realtime mode need pyaudio installed successfully. " + "run `pip install 'ghostos[realtime]'` first" + ) route.realtime = False else: Singleton(realtime_app, RealtimeApp).bind(st.session_state) diff --git a/pyproject.toml b/pyproject.toml index 34f3d18e..1f8dcba6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "ghostos" -version = "0.1.0-beta3" +version = "0.1.0" description = "A framework offers an operating system simulator with a Python Code Interface for AI Agents" authors = ["zhuming ", "Nile Zhou "] license = "MIT" @@ -39,15 +39,20 @@ websockets = "^13.1" pysocks = "^1.7.1" requests = { extras = ["socks"], version = "^2.32.3" } streamlit-paste-button = "^0.1.2" -pyaudio = { version = "^0.2.14" } -spherov2 = { version = "^0.12.1" } +pyaudio = { version = "^0.2.14", optional = true } +spherov2 = { version = "^0.12.1", optional = true } bleak = [ - { version = "^0.22.3", python = ">=3.10,<3.14" } + { version = "^0.22.3", python = ">=3.10,<3.14", optional = true } ] [tool.poetry.scripts] ghostos = "ghostos.scripts.cli:main" +[tool.poetry.extras] +realtime = ['pyaudio'] +sphero = ["spherov2", "bleak", "pyaudio"] + + [tool.poetry.group.dev.dependencies] pytest = "^8.1.1" mypy = "^1.13.0"