Add Discord-native hybrid front-end for Jarvis (bot + bridge)
Some checks failed
Release / semantic-release (push) Successful in 59s
tests / Unit tests (Linux, Python 3.11) (push) Successful in 13m45s
Release / build-linux (push) Failing after 7m47s
Release / build-windows (push) Has been cancelled
Release / build-macos (arm64, macos-latest) (push) Has been cancelled
Release / build-macos (x64, macos-15-intel) (push) Has been cancelled
Release / release-main (push) Has been cancelled
Release / release-develop (push) Has been cancelled
Some checks failed
Release / semantic-release (push) Successful in 59s
tests / Unit tests (Linux, Python 3.11) (push) Successful in 13m45s
Release / build-linux (push) Failing after 7m47s
Release / build-windows (push) Has been cancelled
Release / build-macos (arm64, macos-latest) (push) Has been cancelled
Release / build-macos (x64, macos-15-intel) (push) Has been cancelled
Release / release-main (push) Has been cancelled
Release / release-develop (push) Has been cancelled
Transform isair/jarvis into a Discord-controlled voice assistant running on the Ubuntu VNC desktop, keeping the mature ~39k-line Python brain intact. - bot/ (Node + bun, discord.js): /자비스 slash commands (ephemeral), voice channel join + voice receive/playback, pluggable VNC screen broadcast (selfbot live / noVNC / screenshot) - bridge/ (Python, Flask): wraps jarvis STT + run_reply_engine + Piper TTS behind a thin localhost HTTP API - .env.example, scripts/ (start_bridge/start_bot/dev), README rewrite, docs/language-comparison.md and docs/vnc-xfce-setup.md Language decision: hybrid (Python brain + Node/bun Discord layer) because Discord blocks bot video; native screen broadcast only works via a Node selfbot library.
This commit is contained in:
116
src/jarvis/tools/base.py
Normal file
116
src/jarvis/tools/base.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""Base tool interface for Jarvis tools.
|
||||
|
||||
This module defines the common interface that all tools must implement,
|
||||
ensuring consistency with MCP tool format and enabling dictionary-based execution.
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict, Any, Optional, Callable
|
||||
from .types import ToolExecutionResult
|
||||
|
||||
|
||||
class ToolContext:
|
||||
"""Context object containing all the resources a tool might need."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
db,
|
||||
cfg,
|
||||
system_prompt: str,
|
||||
original_prompt: str,
|
||||
redacted_text: str,
|
||||
max_retries: int,
|
||||
user_print: Callable[[str], None],
|
||||
language: Optional[str] = None,
|
||||
):
|
||||
self.db = db
|
||||
self.cfg = cfg
|
||||
self.system_prompt = system_prompt
|
||||
self.original_prompt = original_prompt
|
||||
self.redacted_text = redacted_text
|
||||
self.max_retries = max_retries
|
||||
self.user_print = user_print
|
||||
# ISO-639-1 code of the language Whisper auto-detected for the current
|
||||
# utterance (e.g. "en", "tr", "de"). None when the tool is invoked
|
||||
# outside the voice path (evals, unit tests, text entry) — tools must
|
||||
# treat absence as "no signal" and fall back to their own default
|
||||
# rather than assuming English.
|
||||
self.language = language
|
||||
|
||||
|
||||
class Tool(ABC):
|
||||
"""Base class for all Jarvis tools.
|
||||
|
||||
This interface matches the MCP tool format with name, description, and inputSchema
|
||||
properties, while providing a simple execution interface focused on tool logic.
|
||||
|
||||
Implementation guideline:
|
||||
- Put all operational logic directly in the `run` method.
|
||||
- Keep helper functions module-level only when they provide clear reuse (e.g. nutrition
|
||||
extraction helpers used by multiple code paths / tests). Otherwise inline.
|
||||
- `run` receives validated args (per schema) and a `ToolContext` giving access to db, cfg,
|
||||
prompts, redacted_text, retry allowance, and a user_print callable.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def name(self) -> str:
|
||||
"""The canonical tool identifier (camelCase)."""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def description(self) -> str:
|
||||
"""Human-readable description of what the tool does."""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def inputSchema(self) -> Dict[str, Any]:
|
||||
"""JSON Schema for tool arguments (matches MCP format)."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def run(self, args: Optional[Dict[str, Any]], context: ToolContext) -> ToolExecutionResult:
|
||||
"""Execute the tool with the given arguments and context.
|
||||
|
||||
This is the only method tools need to implement. All common concerns
|
||||
like user printing, database access, config, etc. are provided via context.
|
||||
|
||||
Args:
|
||||
args: Dictionary containing tool arguments (validated against inputSchema)
|
||||
context: ToolContext with db, cfg, user_print, etc.
|
||||
|
||||
Returns:
|
||||
ToolExecutionResult with execution results
|
||||
"""
|
||||
pass
|
||||
|
||||
def execute(
|
||||
self,
|
||||
db,
|
||||
cfg,
|
||||
tool_args: Optional[Dict[str, Any]],
|
||||
system_prompt: str,
|
||||
original_prompt: str,
|
||||
redacted_text: str,
|
||||
max_retries: int,
|
||||
user_print: Callable[[str], None],
|
||||
language: Optional[str] = None,
|
||||
) -> ToolExecutionResult:
|
||||
"""Execute the tool (internal method used by registry).
|
||||
|
||||
This method creates the context and calls the tool's run method.
|
||||
Tools should implement run(), not this method.
|
||||
"""
|
||||
context = ToolContext(
|
||||
db=db,
|
||||
cfg=cfg,
|
||||
system_prompt=system_prompt,
|
||||
original_prompt=original_prompt,
|
||||
redacted_text=redacted_text,
|
||||
max_retries=max_retries,
|
||||
user_print=user_print,
|
||||
language=language,
|
||||
)
|
||||
return self.run(tool_args, context)
|
||||
Reference in New Issue
Block a user