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
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ repos:
require_serial: true
additional_dependencies: []

- id: ruff
name: ruff
- id: ruff-check
name: ruff-check
description: "Run 'ruff' for extremely fast Python linting"
entry: uv run --dev ruff check
pass_filenames: false
Expand Down
1 change: 1 addition & 0 deletions .ruff.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
target-version = "py310"
exclude = ["docs/**"]
line-length = 120

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ We welcome contributions! These guidelines exist to save everyone time. Followin

## Development Setup

1. Make sure you have `Python 3.12+` installed.
1. Make sure you have `Python 3.10+` installed.
1. Install [uv](https://docs.astral.sh/uv/getting-started/installation/).
1. Fork the repository and clone your fork.
1. Install development dependencies: `make prepare`.
Expand Down
2 changes: 1 addition & 1 deletion examples/jetarm_demo/connect_pychannel_with_rcply.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import asyncio
import argparse
import asyncio

from ghoshell_moss.transports.zmq_channel.zmq_channel import ZMQChannelProxy

Expand Down
6 changes: 3 additions & 3 deletions examples/jetarm_demo/jetarm_agent.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import argparse
import asyncio
from pathlib import Path

from ghoshell_container import Container
import argparse

from ghoshell_moss.core.shell import new_shell
from ghoshell_moss.speech import make_baseline_tts_speech
Expand All @@ -10,8 +11,7 @@
from ghoshell_moss.transports.zmq_channel.zmq_channel import ZMQChannelProxy
from ghoshell_moss_contrib.agent import ModelConf, SimpleAgent
from ghoshell_moss_contrib.agent.chat import ConsoleChat
from ghoshell_moss_contrib.example_ws import workspace_container, get_container
from pathlib import Path
from ghoshell_moss_contrib.example_ws import get_container, workspace_container

ADDRESS = "tcp://192.168.1.15:9527"
"""填入正确的 ip, 需要先对齐 jetarm_ws 运行的机器设备和监听的端口. """
Expand Down
24 changes: 12 additions & 12 deletions examples/miku/main.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import asyncio
import importlib.util
import os
import sys

from ghoshell_moss.speech import make_baseline_tts_speech, Speech
from ghoshell_moss.speech.player.pyaudio_player import PyAudioStreamPlayer
from ghoshell_moss.speech.volcengine_tts import VolcengineTTS, VolcengineTTSConf
from ghoshell_moss_contrib.agent import ModelConf, SimpleAgent

import asyncio
from os.path import dirname, join

import live2d.v3 as live2d
import pygame
from ghoshell_container import Container

from ghoshell_moss.speech import Speech, make_baseline_tts_speech
from ghoshell_moss.speech.player.pyaudio_player import PyAudioStreamPlayer
from ghoshell_moss.speech.volcengine_tts import VolcengineTTS, VolcengineTTSConf
from ghoshell_moss_contrib.agent import ModelConf, SimpleAgent

current_dir = os.path.dirname(os.path.abspath(__file__))
try:
import miku_channels
except ImportError:
if importlib.util.find_spec(miku_channels) is None:
# 加载当前路径.
sys.path.append(current_dir)

import pathlib

from miku_channels.arm import left_arm_chan, right_arm_chan
from miku_channels.body import body_chan
from miku_channels.elbow import left_elbow_chan, right_elbow_chan
Expand All @@ -30,9 +30,9 @@
from miku_channels.leg import left_leg_chan, right_leg_chan
from miku_channels.necktie import necktie_chan
from miku_provider import init_live2d, init_pygame

from ghoshell_moss.core.shell import new_shell
from ghoshell_moss_contrib.example_ws import workspace_container, get_example_speech
import pathlib
from ghoshell_moss_contrib.example_ws import get_example_speech, workspace_container

# 全局状态
WIDTH = 600
Expand Down
10 changes: 5 additions & 5 deletions examples/miku/miku_provider.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio
import importlib.util
import os
import sys
from os.path import dirname, join
Expand All @@ -7,10 +8,8 @@
import pygame
from ghoshell_container import Container, get_container

try:
import miku_channels
except ImportError:
current_dir = os.path.dirname(os.path.abspath(__file__))
current_dir = os.path.dirname(os.path.abspath(__file__))
if importlib.util.find_spec(miku_channels) is None:
sys.path.append(current_dir)

from miku_channels.arm import left_arm_chan, right_arm_chan
Expand All @@ -22,8 +21,9 @@
from miku_channels.head import head_chan
from miku_channels.leg import left_leg_chan, right_leg_chan
from miku_channels.necktie import necktie_chan
from ghoshell_moss.transports.zmq_channel import ZMQChannelProvider

from ghoshell_moss import Channel
from ghoshell_moss.transports.zmq_channel import ZMQChannelProvider

# 全局状态
model: live2d.LAppModel | None = None
Expand Down
29 changes: 12 additions & 17 deletions examples/moss_agent.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import os.path
import pathlib
import asyncio
import pathlib

from ghoshell_common.contracts import Workspace, LoggerItf
from ghoshell_common.contracts import LoggerItf, Workspace
from ghoshell_container import Container

from ghoshell_moss_contrib.example_ws import workspace_container, get_example_speech
from ghoshell_moss.channels.mac_channel import new_mac_control_channel
from ghoshell_moss_contrib.channels.mermaid_draw import new_mermaid_chan
from ghoshell_moss_contrib.channels.web_bookmark import build_web_bookmark_chan

from ghoshell_moss_contrib.agent import SimpleAgent, ModelConf, ConsoleChat
from ghoshell_moss.core.shell import new_shell
from ghoshell_moss.transports.zmq_channel.zmq_hub import ZMQChannelHub, ZMQHubConfig, ZMQProxyConfig

# 不着急删除, 方便自测时开启.
from ghoshell_moss_contrib.channels.screen_capture import ScreenCapture
from ghoshell_moss.transports.zmq_channel.zmq_hub import ZMQChannelProxy
from ghoshell_moss.transports.zmq_channel.zmq_hub import ZMQChannelHub, ZMQHubConfig, ZMQProxyConfig
from ghoshell_moss_contrib.agent import ConsoleChat, ModelConf, SimpleAgent
from ghoshell_moss_contrib.channels.mermaid_draw import new_mermaid_chan
from ghoshell_moss_contrib.channels.web_bookmark import build_web_bookmark_chan
from ghoshell_moss_contrib.example_ws import get_example_speech, workspace_container

"""
说明:
Expand Down Expand Up @@ -118,11 +113,11 @@ def run_moss_agent(container: Container):
instruction=instructions,
chat=ConsoleChat(logger=logger),
model=ModelConf(
kwargs=dict(
thinking=dict(
type="disabled",
)
),
kwargs={
"thinking": {
"type": "disabled",
},
},
),
shell=shell,
)
Expand Down
13 changes: 6 additions & 7 deletions examples/moss_zmq_channels/miku_app.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
from os.path import dirname, join
import sys
from os.path import dirname, join

# patch miku 的读取路径.
# 由于 miku 还是一个实验性的数字人项目, 暂时不希望把它打包到 ghoshell_moss_contrib 里 (太大)
# 所以先用比较脏的相对路径来读取它.
current_dir = dirname(__file__)
workspace_dir = join(dirname(current_dir), ".workspace")
try:
import miku
except ImportError:
miku_dir = join(dirname(current_dir), "miku")
print(miku_dir)
miku_dir = join(dirname(current_dir), "miku")
if miku_dir not in sys.path:
sys.path.append(miku_dir)
from miku_provider import run_game_with_zmq_provider

import asyncio

from miku_provider import run_game_with_zmq_provider

from ghoshell_moss_contrib.example_ws import workspace_container

if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion examples/moss_zmq_channels/vision_app.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from ghoshell_moss_contrib.channels.opencv_vision import OpenCVVision
from ghoshell_moss import get_container
from ghoshell_moss.transports.zmq_channel import ZMQChannelProvider
from ghoshell_moss_contrib.channels.opencv_vision import OpenCVVision

if __name__ == "__main__":
# 初始化容器
Expand Down
2 changes: 1 addition & 1 deletion examples/vision_exam/vision_provider.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from ghoshell_moss_contrib.channels.opencv_vision import OpenCVVision
from ghoshell_moss import get_container
from ghoshell_moss.transports.zmq_channel import ZMQChannelProvider
from ghoshell_moss_contrib.channels.opencv_vision import OpenCVVision

if __name__ == "__main__":
# 初始化容器
Expand Down
2 changes: 1 addition & 1 deletion examples/vision_exam/vision_proxy.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import asyncio

from ghoshell_moss.message.contents import Base64Image
from ghoshell_moss.transports.zmq_channel.zmq_channel import ZMQChannelProxy
from ghoshell_moss_contrib.gui.image_viewer import SimpleImageViewer, run_img_viewer
from ghoshell_moss.message.contents import Base64Image

if __name__ == "__main__":
# 测试专用.
Expand Down
4 changes: 2 additions & 2 deletions src/ghoshell_moss/core/concepts/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ class CommandTaskStateType(str, Enum):

@classmethod
def is_complete(cls, state: str | Self) -> bool:
return state == cls.done or state == cls.failed or state == cls.cancelled
return state in (cls.done, cls.failed, cls.cancelled)

@classmethod
def is_stopped(cls, state: str | Self) -> bool:
return state == cls.cancelled or state == cls.failed
return state in (cls.cancelled, cls.failed)


class CommandTaskState(str, Enum):
Expand Down
2 changes: 1 addition & 1 deletion src/ghoshell_moss/speech/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from ghoshell_common.contracts import LoggerItf

from ghoshell_moss.core.concepts.speech import TTS, StreamAudioPlayer, Speech, SpeechStream
from ghoshell_moss.core.concepts.speech import TTS, Speech, SpeechStream, StreamAudioPlayer
from ghoshell_moss.speech.mock import MockSpeech
from ghoshell_moss.speech.stream_tts_speech import TTSSpeech, TTSSpeechStream

Expand Down
2 changes: 1 addition & 1 deletion src/ghoshell_moss/speech/mock.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import threading
import time
from queue import Empty, Queue
from typing import Optional

from ghoshell_common.helpers import uuid

from ghoshell_moss.core.concepts.speech import Speech, SpeechStream
from ghoshell_moss.core.helpers.asyncio_utils import ThreadSafeEvent
import time


class MockSpeechStream(SpeechStream):
Expand Down
30 changes: 15 additions & 15 deletions src/ghoshell_moss_contrib/channels/opencv_vision.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import logging
import threading
import time
from datetime import datetime
from typing import List, Optional, Tuple
from PIL import Image
import cv2
from typing import Optional

from ghoshell_moss.message import Message, Base64Image, Text
import cv2
from ghoshell_common.contracts import LoggerItf
from ghoshell_container import IoCContainer, get_container
from PIL import Image

from ghoshell_moss import PyChannel
from ghoshell_moss.transports.zmq_channel.zmq_channel import ZMQChannelProvider
import logging
from ghoshell_moss.message import Base64Image, Message, Text


class OpenCVVision:
Expand Down Expand Up @@ -58,16 +58,16 @@ def _initialize_camera(self) -> bool:
# 测试读取一帧
ret, _ = self._cap.read()
if ret:
self._logger.info(f"摄像头初始化成功,使用索引 {camera_index}")
self._logger.info("摄像头初始化成功,使用索引 %s", camera_index)
return True
else:
self._cap.release()

self._logger.error("无法初始化任何摄像头")
return False

except Exception as e:
self._logger.error(f"摄像头初始化失败: {e}")
except Exception:
self._logger.exception("摄像头初始化失败")
return False

def _capture_frame(self) -> bool:
Expand Down Expand Up @@ -152,15 +152,15 @@ def close(self) -> None:
# 确保窗口被销毁
try:
cv2.destroyWindow(self._window_name)
except:
pass
except Exception:
self._logger.debug("销毁 OpenCV 窗口失败: %s", self._window_name, exc_info=True)

# 最后调用 destroyAllWindows 确保清理所有窗口
cv2.destroyAllWindows()

self._logger.info("视觉模块已关闭")

def get_cached_image(self) -> Tuple[Optional[Image.Image], float]:
def get_cached_image(self) -> tuple[Optional[Image.Image], float]:
"""
线程安全地获取缓存的图像和时间戳

Expand Down Expand Up @@ -194,7 +194,7 @@ async def start_looking(self) -> None:
self._is_caching_image = True
self._last_capture_time = 0.0 # 重置时间,立即捕获下一帧

async def context_messages(self) -> List[Message]:
async def context_messages(self) -> list[Message]:
"""
返回最新的视觉信息作为上下文消息

Expand Down Expand Up @@ -301,7 +301,7 @@ def run_opencv_loop(self) -> None:

except KeyboardInterrupt:
self._logger.info("收到键盘中断信号")
except Exception as e:
self._logger.error(f"视觉模块运行异常: {e}")
except Exception:
self._logger.exception("视觉模块运行异常")
finally:
self.close()
15 changes: 8 additions & 7 deletions src/ghoshell_moss_contrib/example_ws.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import logging
import os
from contextlib import contextmanager
from pathlib import Path
from typing import List
from ghoshell_container import Provider, Container, set_container, get_container

from ghoshell_common.contracts import LocalWorkspaceProvider, LoggerItf, WorkspaceConfigsProvider, Workspace
from ghoshell_common.contracts import LocalWorkspaceProvider, LoggerItf, WorkspaceConfigsProvider
from ghoshell_container import Container, Provider, get_container, set_container

from ghoshell_moss.core import Speech
from pathlib import Path
from contextlib import contextmanager
import logging

__all__ = [
"get_container",
"set_container",
"get_example_speech",
"init_container",
"set_container",
"workspace_container",
"get_example_speech",
]


Expand Down
11 changes: 6 additions & 5 deletions src/ghoshell_moss_contrib/gui/image_viewer.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import sys
import threading
from typing import Callable
from collections.abc import Callable

from PIL import Image
from PyQt6.QtCore import QObject, Qt, pyqtSignal
from PyQt6.QtGui import QImage, QPixmap
from PyQt6.QtWidgets import QApplication, QLabel, QMainWindow
from PyQt6.QtGui import QPixmap, QImage
from PyQt6.QtCore import Qt, pyqtSignal, QObject
from PIL import Image, ImageDraw

__all__ = ["SimpleImageViewer", "run_img_viewer"]

Expand Down Expand Up @@ -46,7 +47,7 @@ def _update_pixmap(self):
)
self.label.setPixmap(scaled)

def resizeEvent(self, event):
def resizeEvent(self, event): # noqa: N802
self._update_pixmap()
super().resizeEvent(event)

Expand Down
Loading