Skip to content
Closed
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
10 changes: 3 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,10 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
# Pin ruff: 0.16.0 (released 2026-07-23) turned on a large new rule
# surface that flags ~460 pre-existing findings across the repo and
# breaks CI on every PR. Pinned to the last green version; bump only
# once those findings are addressed (see python-sdk-test for the twin).
pip install ruff==0.15.22 mypy
pip install "ruff~=0.16.7" mypy
if [ -f examples/python/requirements.txt ]; then pip install -r examples/python/requirements.txt; fi
- name: Lint with ruff
run: ruff check . --extend-exclude '**/*.ipynb'
run: ruff check . --extend-exclude '*.ipynb'
- name: Type check with mypy
run: mypy examples/python apps/pipecat-moss apps/livekit-moss-vercel --ignore-missing-imports --exclude "apps/pipecat-moss/hume-ollama-local/create_index.py"

Expand Down Expand Up @@ -95,7 +91,7 @@ jobs:
- name: Lint with ruff
working-directory: sdks/python/sdk
run: |
pip install ruff==0.15.22 # pinned; see python-lint job for why (ruff 0.16.0 regression)
pip install "ruff~=0.16.7"
ruff check src/ tests/
- name: Type check with mypy
working-directory: sdks/python/sdk
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ skills-lock.json

# Swift / SwiftPM build artifacts
.build/

# Python SDK setuptools build output
sdks/python/sdk/build/
32 changes: 16 additions & 16 deletions apps/agora-custom-llm-moss/server/src/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import uuid
from contextlib import asynccontextmanager
from pathlib import Path
from typing import Any, Dict, List, Optional, Union
from typing import Any

from dotenv import load_dotenv
from fastapi import FastAPI, Header, HTTPException
Expand Down Expand Up @@ -54,36 +54,36 @@ class TextContent(BaseModel):

class SystemMessage(BaseModel):
role: str = "system"
content: Union[str, List[str]]
content: str | list[str]


class UserMessage(BaseModel):
role: str = "user"
content: Union[str, List[Union[TextContent, Dict]]]
content: str | list[TextContent | dict]


class AssistantMessage(BaseModel):
role: str = "assistant"
content: Union[str, List[TextContent], None] = None
tool_calls: Optional[List[Dict]] = None
content: str | list[TextContent] | None = None
tool_calls: list[dict] | None = None


class ToolMessage(BaseModel):
role: str = "tool"
content: Union[str, List[str]]
content: str | list[str]
tool_call_id: str


class ChatCompletionRequest(BaseModel):
model: Optional[str] = None
messages: List[Union[SystemMessage, UserMessage, AssistantMessage, ToolMessage]]
model: str | None = None
messages: list[SystemMessage | UserMessage | AssistantMessage | ToolMessage]
stream: bool = True
stream_options: Optional[Dict] = None
temperature: Optional[float] = None
max_tokens: Optional[int] = None
tools: Optional[List[Dict]] = None
tool_choice: Optional[Union[str, Dict]] = None
response_format: Optional[Dict] = None
stream_options: dict | None = None
temperature: float | None = None
max_tokens: int | None = None
tools: list[dict] | None = None
tool_choice: str | dict | None = None
response_format: dict | None = None


def load_server_env(server_dir: Path | None = None) -> None:
Expand Down Expand Up @@ -164,7 +164,7 @@ async def open_moss():
return session


def require_bearer(authorization: Optional[str], mock: bool) -> None:
def require_bearer(authorization: str | None, mock: bool) -> None:
if mock:
return
if not authorization or not authorization.lower().startswith("bearer "):
Expand Down Expand Up @@ -320,7 +320,7 @@ async def lifespan(_app: FastAPI):
@app.post("/chat/completions")
async def chat_completions(
request: ChatCompletionRequest,
authorization: Optional[str] = Header(None, alias="Authorization"),
authorization: str | None = Header(None, alias="Authorization"),
):
require_bearer(authorization, mock=mock)
if not request.stream:
Expand Down
1 change: 0 additions & 1 deletion apps/agora-custom-llm-moss/server/src/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import os

from fastapi import FastAPI

from llm import create_app, load_server_env

load_server_env()
Expand Down
1 change: 1 addition & 0 deletions apps/docker/python/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import asyncio
import os

from dotenv import load_dotenv
from moss import MossClient, QueryOptions

Expand Down
12 changes: 6 additions & 6 deletions apps/moss-llamaindex/backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@
"""

import asyncio
import json
import os
import tempfile
import time
import uuid
import json
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
from typing import AsyncGenerator

from fastapi import FastAPI, APIRouter, File, UploadFile, HTTPException
from dotenv import load_dotenv
from fastapi import APIRouter, FastAPI, File, HTTPException, UploadFile
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from dotenv import load_dotenv
from moss import MossClient, DocumentInfo, QueryOptions
from liteparse import LiteParse
from moss import DocumentInfo, MossClient, QueryOptions
from nltk.tokenize import sent_tokenize
from pydantic import BaseModel

load_dotenv()

Expand Down
2 changes: 1 addition & 1 deletion apps/moss-llamaindex/backend/seed_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
from pathlib import Path

from dotenv import load_dotenv
from moss import MossClient, DocumentInfo
from liteparse import LiteParse
from moss import DocumentInfo, MossClient
from nltk.tokenize import sent_tokenize

load_dotenv()
Expand Down
2 changes: 2 additions & 0 deletions apps/ten-moss/ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# see apps/ten-moss/README.md "Provenance"). Its runtime/agent modules ship with
# star-imports and package re-exports upstream, which we don't restyle. Silence
# those vendored-code lints; the Moss delta (extension.py, config.py) stays linted.
extend = "../../ruff.toml"

[lint.per-file-ignores]
"tenapp/ten_packages/extension/main_python/__init__.py" = ["F401"]
"tenapp/ten_packages/extension/main_python/agent/*.py" = ["F401", "F403", "F405", "F841"]
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
#
from ten_runtime import (
Addon,
register_addon_as_extension,
TenEnv,
register_addon_as_extension,
)


Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import asyncio
import json
from typing import Awaitable, Callable, Optional
from .llm_exec import LLMExec
from ten_runtime import AsyncTenEnv, Cmd, CmdResult, Data, StatusCode
from collections.abc import Awaitable, Callable
from typing import Optional

from ten_ai_base.types import LLMToolMetadata
from ten_runtime import AsyncTenEnv, Cmd, CmdResult, Data, StatusCode

from .events import *
from .llm_exec import LLMExec


class Agent:
Expand All @@ -22,9 +25,9 @@ def __init__(self, ten_env: AsyncTenEnv):
self._llm_queue: asyncio.Queue[LLMResponseEvent] = asyncio.Queue()

# Current consumer tasks
self._asr_consumer: Optional[asyncio.Task] = None
self._llm_consumer: Optional[asyncio.Task] = None
self._llm_active_task: Optional[asyncio.Task] = (
self._asr_consumer: asyncio.Task | None = None
self._llm_consumer: asyncio.Task | None = None
self._llm_active_task: asyncio.Task | None = (
None # currently running handler
)

Expand All @@ -44,7 +47,7 @@ def __init__(self, ten_env: AsyncTenEnv):
def on(
self,
event_type: AgentEvent,
handler: Callable[[AgentEvent], Awaitable] = None,
handler: Callable[[AgentEvent], Awaitable] | None = None,
):
"""
Register a callback for a given event type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ async def on_asr(self, event: ASRResultEvent): ...
"""

def wrapper(func):
setattr(func, "_agent_event_type", event_type)
func._agent_event_type = event_type
return func

return wrapper
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from typing import Any, Literal

from pydantic import BaseModel
from typing import Literal, Union, Dict, Any
from ten_ai_base.types import LLMToolMetadata


# ==== Base Event ====


Expand Down Expand Up @@ -49,7 +49,7 @@ class ASRResultEvent(AgentEventBase):
name: Literal["asr_result"] = "asr_result"
text: str
final: bool
metadata: Dict[str, Any]
metadata: dict[str, Any]


class LLMResponseEvent(AgentEventBase):
Expand All @@ -64,10 +64,10 @@ class LLMResponseEvent(AgentEventBase):

# ==== Unified Event Union ====

AgentEvent = Union[
UserJoinedEvent,
UserLeftEvent,
ToolRegisterEvent,
ASRResultEvent,
LLMResponseEvent,
]
AgentEvent = (
UserJoinedEvent
| UserLeftEvent
| ToolRegisterEvent
| ASRResultEvent
| LLMResponseEvent
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
import asyncio
import json
import traceback
from typing import Awaitable, Callable, Literal, Optional
import uuid
from collections.abc import Awaitable, Callable
from typing import Literal, Optional

from ten_ai_base.const import CMD_PROPERTY_RESULT
from ten_ai_base.helper import AsyncQueue
from ten_ai_base.struct import (
Expand All @@ -24,9 +27,9 @@
parse_llm_response,
)
from ten_ai_base.types import LLMToolMetadata, LLMToolResult
from ..helper import _send_cmd, _send_cmd_ex
from ten_runtime import AsyncTenEnv, Loc, StatusCode
import uuid

from ..helper import _send_cmd, _send_cmd_ex


class LLMExec:
Expand All @@ -39,16 +42,10 @@ def __init__(self, ten_env: AsyncTenEnv):
self.ten_env = ten_env
self.input_queue = AsyncQueue()
self.stopped = False
self.on_response: Optional[
Callable[[AsyncTenEnv, str, str, bool], Awaitable[None]]
] = None
self.on_reasoning_response: Optional[
Callable[[AsyncTenEnv, str, str, bool], Awaitable[None]]
] = None
self.on_tool_call: Optional[
Callable[[AsyncTenEnv, LLMToolMetadata], Awaitable[None]]
] = None
self.current_task: Optional[asyncio.Task] = None
self.on_response: Callable[[AsyncTenEnv, str, str, bool], Awaitable[None]] | None = None
self.on_reasoning_response: Callable[[AsyncTenEnv, str, str, bool], Awaitable[None]] | None = None
self.on_tool_call: Callable[[AsyncTenEnv, LLMToolMetadata], Awaitable[None]] | None = None
self.current_task: asyncio.Task | None = None
self.loop = asyncio.get_event_loop()
self.loop.create_task(self._process_input_queue())
self.available_tools: list[LLMToolMetadata] = []
Expand All @@ -57,7 +54,7 @@ def __init__(self, ten_env: AsyncTenEnv):
asyncio.Lock()
) # Lock to ensure thread-safe access
self.contexts: list[LLMMessage] = []
self.current_request_id: Optional[str] = None
self.current_request_id: str | None = None
self.current_text = None

async def queue_input(self, item: str) -> None:
Expand Down Expand Up @@ -169,14 +166,17 @@ async def _send_to_llm(
await self._queue_context(ten_env, new_message)

async for cmd_result, _ in response:
if cmd_result and cmd_result.is_final() is False:
if cmd_result.get_status_code() == StatusCode.OK:
response_json, _ = cmd_result.get_property_to_json(None)
ten_env.log_info(
f"_send_to_llm: response_json {response_json}"
)
completion = parse_llm_response(response_json)
await self._handle_llm_response(completion)
if (
cmd_result
and cmd_result.is_final() is False
and cmd_result.get_status_code() == StatusCode.OK
):
response_json, _ = cmd_result.get_property_to_json(None)
ten_env.log_info(
f"_send_to_llm: response_json {response_json}"
)
completion = parse_llm_response(response_json)
await self._handle_llm_response(completion)

async def _handle_llm_response(self, llm_output: LLMResponse | None):
self.ten_env.log_info(f"_handle_llm_response: {llm_output}")
Expand Down
Loading
Loading