Compare commits
No commits in common. "ba84ca1a3a678b6bff467c0a8eb520fc2760aca1" and "a56fd15d6739343c0b81a4b598c32ad22ed8d18e" have entirely different histories.
ba84ca1a3a
...
a56fd15d67
21
LICENSE
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2026 Dita Aji Pratama
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
1
agent/characters/musashi/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
service.yaml
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
# This file serves as a template for character-specific service overrides.
|
|
||||||
# Copy this to agent/characters/<character>/service.yaml and fill in the credentials.
|
|
||||||
|
|
||||||
xmpp:
|
|
||||||
enabled: false
|
|
||||||
username: ""
|
|
||||||
password: ""
|
|
||||||
muc_rooms: # YAML list, e.g.:
|
|
||||||
# - "room1@conference.server"
|
|
||||||
# - "room2@conference.server"
|
|
||||||
nickname: "" # custom MUC nickname (empty = use username)
|
|
||||||
selective_response: true # true = only response if mentioned/relevant
|
|
||||||
debug: false # true = tampilkan log debug menyeluruh (semua stanza XML, lifecycle, dll)
|
|
||||||
mute_session_end: false # true = tidak kirim pesan saat sesi berakhir (timeout)
|
|
||||||
|
|
||||||
telegram:
|
|
||||||
enabled: false
|
|
||||||
token: ""
|
|
||||||
allowed_group_ids: # YAML list, empty = all group. e.g.:
|
|
||||||
# - "123456789"
|
|
||||||
# - "987654321"
|
|
||||||
selective_response: true # true = only response if mentioned/relevant
|
|
||||||
mute_session_end: false # true = tidak kirim pesan saat sesi berakhir (timeout)
|
|
||||||
@ -2,9 +2,9 @@ import os, sys, threading, time, signal
|
|||||||
|
|
||||||
import config
|
import config
|
||||||
|
|
||||||
from lib import gadget, personality, moop, workspace, ragroleplay as ragroleplay_lib
|
from lib import gadget, personality, moop, ragroleplay as ragroleplay_lib
|
||||||
from modules import argsparser
|
from modules import argsparser
|
||||||
from tools import coder, carrack, ragroleplay as ragroleplay_tools, vision, imagegen, smart, smart_rp, syshendrik
|
from tools import coder, carrack, ragroleplay as ragroleplay_tools, vision, imagegen, smart, smart_rp
|
||||||
|
|
||||||
from services.llm_client import LLMClient
|
from services.llm_client import LLMClient
|
||||||
from services.xmpp_client import XMPPClient
|
from services.xmpp_client import XMPPClient
|
||||||
@ -20,6 +20,7 @@ tools_definition = [
|
|||||||
gadget.tools_mapping( schema = coder.schema_run_bash, handler = coder.run_bash ),
|
gadget.tools_mapping( schema = coder.schema_run_bash, handler = coder.run_bash ),
|
||||||
gadget.tools_mapping( schema = coder.schema_search_code, handler = coder.search_code ),
|
gadget.tools_mapping( schema = coder.schema_search_code, handler = coder.search_code ),
|
||||||
gadget.tools_mapping( schema = coder.schema_git_operation, handler = coder.git_operation ),
|
gadget.tools_mapping( schema = coder.schema_git_operation, handler = coder.git_operation ),
|
||||||
|
gadget.tools_mapping( schema = coder.schema_workspace_change, handler = coder.workspace_change ),
|
||||||
gadget.tools_mapping( schema = coder.schema_search_grep, handler = coder.search_grep ),
|
gadget.tools_mapping( schema = coder.schema_search_grep, handler = coder.search_grep ),
|
||||||
gadget.tools_mapping( schema = coder.schema_search_glob, handler = coder.search_glob ),
|
gadget.tools_mapping( schema = coder.schema_search_glob, handler = coder.search_glob ),
|
||||||
# Carrack Tools
|
# Carrack Tools
|
||||||
@ -46,8 +47,6 @@ tools_definition = [
|
|||||||
gadget.tools_mapping( schema = ragroleplay_tools.schema_memories_delete, handler = ragroleplay_tools.memories_delete ),
|
gadget.tools_mapping( schema = ragroleplay_tools.schema_memories_delete, handler = ragroleplay_tools.memories_delete ),
|
||||||
|
|
||||||
gadget.tools_mapping( schema = ragroleplay_tools.schema_end_session, handler = ragroleplay_tools.end_session ),
|
gadget.tools_mapping( schema = ragroleplay_tools.schema_end_session, handler = ragroleplay_tools.end_session ),
|
||||||
# SysHendrik Tools
|
|
||||||
gadget.tools_mapping( schema = syshendrik.schema_workspace_change, handler = syshendrik.workspace_change ),
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -68,7 +67,7 @@ def main():
|
|||||||
if not os.path.isdir(resolved):
|
if not os.path.isdir(resolved):
|
||||||
print(f"Error: '{resolved}' is not a valid directory", flush=True)
|
print(f"Error: '{resolved}' is not a valid directory", flush=True)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
workspace.set_current_workspace(resolved)
|
coder.set_current_workspace(resolved)
|
||||||
|
|
||||||
# Inference
|
# Inference
|
||||||
llm_client = LLMClient("", "", "", config.llm_timeout)
|
llm_client = LLMClient("", "", "", config.llm_timeout)
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import curses
|
|||||||
import os
|
import os
|
||||||
import config
|
import config
|
||||||
from .agent import log
|
from .agent import log
|
||||||
from lib import workspace
|
from tools.coder import set_current_workspace
|
||||||
from . import keycodes
|
from . import keycodes
|
||||||
from .moopui import (
|
from .moopui import (
|
||||||
model_selector_popup,
|
model_selector_popup,
|
||||||
@ -157,7 +157,7 @@ def workspace_popup(app, stdscr):
|
|||||||
if ws:
|
if ws:
|
||||||
resolved = os.path.abspath(ws)
|
resolved = os.path.abspath(ws)
|
||||||
if os.path.isdir(resolved):
|
if os.path.isdir(resolved):
|
||||||
workspace.set_current_workspace(resolved)
|
set_current_workspace(resolved)
|
||||||
log(app, "system", f"Workspace \u2192 {resolved}")
|
log(app, "system", f"Workspace \u2192 {resolved}")
|
||||||
else:
|
else:
|
||||||
log(app, "error", f"Invalid directory: {resolved}")
|
log(app, "error", f"Invalid directory: {resolved}")
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import curses
|
import curses
|
||||||
import json
|
import json
|
||||||
from lib import workspace
|
from tools.coder import get_current_workspace
|
||||||
|
|
||||||
from . import theme
|
from . import theme
|
||||||
from .actions import ACTIONS
|
from .actions import ACTIONS
|
||||||
@ -361,7 +361,7 @@ def draw_status(app, stdscr):
|
|||||||
mode = " PROCESSING " if app.processing else " READY "
|
mode = " PROCESSING " if app.processing else " READY "
|
||||||
set_name = (getattr(app, "model_set_name", "") or "-").strip() or "-"
|
set_name = (getattr(app, "model_set_name", "") or "-").strip() or "-"
|
||||||
model = (app.llm.model or "-").strip() or "-"
|
model = (app.llm.model or "-").strip() or "-"
|
||||||
ws = workspace.get_current_workspace() or "-"
|
ws = get_current_workspace() or "-"
|
||||||
|
|
||||||
send_key = ACTIONS["send_prompt"].get("shortcut") or "Ctrl+Return"
|
send_key = ACTIONS["send_prompt"].get("shortcut") or "Ctrl+Return"
|
||||||
right = f" {app.character_name} {send_key}:Send "
|
right = f" {app.character_name} {send_key}:Send "
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
# Pemilik tunggal state workspace sesi agent.
|
|
||||||
# Modul lain (tools.coder, tools.syshendrik, tools.vision, interfaces.tui)
|
|
||||||
# membaca/mengganti workspace lewat getter/setter di sini, bukan menyimpan
|
|
||||||
# salinan state sendiri, agar tidak ada dua sumber kebenaran.
|
|
||||||
|
|
||||||
CURRENT_WORKSPACE = os.getcwd()
|
|
||||||
|
|
||||||
def get_current_workspace():
|
|
||||||
global CURRENT_WORKSPACE
|
|
||||||
return CURRENT_WORKSPACE
|
|
||||||
|
|
||||||
def set_current_workspace(path):
|
|
||||||
global CURRENT_WORKSPACE
|
|
||||||
# chdir dulu agar state hanya ter-update ketika perpindahan benar-benar sukses
|
|
||||||
os.chdir(path)
|
|
||||||
CURRENT_WORKSPACE = path
|
|
||||||
@ -1,78 +0,0 @@
|
|||||||
# Feature: Mute Session End Notification
|
|
||||||
|
|
||||||
## Goal
|
|
||||||
Tambah field konfigurasi baru per-service (xmpp & telegram) untuk menonaktifkan
|
|
||||||
pesan pemberitahuan saat sesi berakhir. Jika `true`, agent tidak mengirim pesan
|
|
||||||
"pemberitahuan sesi berakhir" (`*session end*` / `Sesi ditutup. Sampai jumpa`)
|
|
||||||
ketika sesi ditutup karena timeout/kegiatan berhenti.
|
|
||||||
|
|
||||||
Field mengikuti pola yang sudah ada di `service.yaml` (sudah mendukung override
|
|
||||||
per karakter untuk xmpp & telegram).
|
|
||||||
|
|
||||||
## Field
|
|
||||||
- Nama: `mute_session_end`
|
|
||||||
- Tipe: bool
|
|
||||||
- Default: `false` (perilaku lama: tetap kirim pesan penutup)
|
|
||||||
- Letak: di bawah section `xmpp:` dan `telegram:` (service.yaml & config.yaml)
|
|
||||||
- Konstanta global baru: `XMPP_MUTE_SESSION_END`, `TELEGRAM_MUTE_SESSION_END`
|
|
||||||
|
|
||||||
## Scope pesan yang di-mute (jalur target)
|
|
||||||
Hanya pesan penutup yang dihasilkan oleh sistem saat sesi berakhir otomatis:
|
|
||||||
- `xmpp_client._timeout_session`
|
|
||||||
- `telegram_client._timeout_session`
|
|
||||||
|
|
||||||
Sesi tetap di-reset seperti semula; hanya pengiriman teks penutupnya yang dilewati.
|
|
||||||
|
|
||||||
**Dikonfirmasi:** natural-close via tool `end_session` TIDAK ikut di-mute (farewell
|
|
||||||
adalah bagian percakapan model, bukan pesan sistem).
|
|
||||||
|
|
||||||
## Perubahan tambahan: timeout group/MUC
|
|
||||||
Diperpanjang dari 5 menit (300s) menjadi 30 menit (1800s) untuk group/MUC.
|
|
||||||
Titik yang diubah:
|
|
||||||
- `telegram_client.py` (`_process_message`): `... else 300` → `1800` (chat_type group).
|
|
||||||
- `xmpp_client.py`: dua `session.start_timer(300, ..., 'groupchat')` → `1800`.
|
|
||||||
Private/DM tetap 86400 (di luar scope).
|
|
||||||
|
|
||||||
## Files to change
|
|
||||||
|
|
||||||
### 1. config.py
|
|
||||||
- Tambah pembacaan default dari `config.yaml` pada blok modul-level:
|
|
||||||
- `XMPP_MUTE_SESSION_END` (xmpp section, default "false")
|
|
||||||
- `TELEGRAM_MUTE_SESSION_END` (telegram section, default "false")
|
|
||||||
- Tambah keduanya ke `global` + reset di `_reset_service_from_config()`.
|
|
||||||
- Tambah keduanya ke `global` + aplikasi override di `load_service_config()`
|
|
||||||
memakai pola yang sama seperti field lain:
|
|
||||||
- `if "mute_session_end" in xmpp_data: XMPP_MUTE_SESSION_END = _to_bool(...)`
|
|
||||||
- `if "mute_session_end" in telegram_data: TELEGRAM_MUTE_SESSION_END = _to_bool(...)`
|
|
||||||
- Parsing bool pakai pola string existing: `str(v).strip().lower() in ("true","1","yes")`.
|
|
||||||
|
|
||||||
### 2. services/xmpp_client.py (`_timeout_session`)
|
|
||||||
- Bungkus `self._schedule_send(session_id, body, mtype)` dengan guard:
|
|
||||||
`if not config.XMPP_MUTE_SESSION_END:` — tetap panggil `self._session_mgr.reset(session_id)`.
|
|
||||||
- Opsional: log bahwa notifikasi di-mute.
|
|
||||||
|
|
||||||
### 3. services/telegram_client.py (`_timeout_session`)
|
|
||||||
- Bungkus `self._schedule_send(chat_id, body)` dengan guard:
|
|
||||||
`if not config.TELEGRAM_MUTE_SESSION_END:` — tetap `self._session_mgr.reset(...)`.
|
|
||||||
|
|
||||||
### 4. Templates (dokumentasi, agar field mudah ditemukan)
|
|
||||||
- `config.default.yaml`: tambah `mute_session_end: false` di section xmpp & telegram.
|
|
||||||
- `agent/characters/*/service.default.yaml` (semua karakter): tambah field yang sama.
|
|
||||||
- Catatan: `service.yaml` runtime bersifat gitignored; karena loader cek keberadaan
|
|
||||||
key, karakter tanpa key baru akan fallback ke default `false` (backward compatible).
|
|
||||||
|
|
||||||
## Backward compatibility
|
|
||||||
- Tidak ada service.yaml existing yang wajib diubah.
|
|
||||||
- Key hilang => default `false` => behavior identik dengan sekarang.
|
|
||||||
|
|
||||||
## Validation plan
|
|
||||||
- `python -c "import config; print(config.XMPP_MUTE_SESSION_END, config.TELEGRAM_MUTE_SESSION_END)"`
|
|
||||||
untuk default.
|
|
||||||
- Load karakter dengan `mute_session_end: true` (via service.yaml test / char override)
|
|
||||||
dan verifikasi konstanta menjadi True.
|
|
||||||
- Kode review manual kedua `_timeout_session`: saat True, tidak ada `send`/`schedule_send`,
|
|
||||||
sesi tetap reset.
|
|
||||||
|
|
||||||
## Resolved
|
|
||||||
Scope final: hanya `_timeout_session`. Natural-close `end_session` tidak disentuh
|
|
||||||
(sudah dikonfirmasi user). Timeout group/MUC dinaikkan ke 30 menit.
|
|
||||||
|
Before Width: | Height: | Size: 9.1 KiB |
@ -1,38 +0,0 @@
|
|||||||
@startmindmap
|
|
||||||
|
|
||||||
<style>
|
|
||||||
mindmapDiagram {
|
|
||||||
//FontColor white
|
|
||||||
.done {
|
|
||||||
BackgroundColor green
|
|
||||||
BackgroundColor lightgreen
|
|
||||||
//FontColor black
|
|
||||||
}
|
|
||||||
.unknown {
|
|
||||||
BackgroundColor #FFBBCC
|
|
||||||
BackgroundColor purple
|
|
||||||
FontColor white
|
|
||||||
}
|
|
||||||
.skip {
|
|
||||||
BackgroundColor #FFBBCC
|
|
||||||
BackgroundColor red
|
|
||||||
FontColor white
|
|
||||||
}
|
|
||||||
.info {
|
|
||||||
BackgroundColor blue
|
|
||||||
BackgroundColor lightblue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
;top to bottom direction
|
|
||||||
|
|
||||||
* TUI/GUI/API Settings
|
|
||||||
** TUI
|
|
||||||
*** Parameters for tokenizer
|
|
||||||
*** Service list
|
|
||||||
****_ Kontak dengan daemon
|
|
||||||
*** Model set & Provider list <<done>>
|
|
||||||
*** Roleplayer user ID
|
|
||||||
|
|
||||||
@endmindmap
|
|
||||||
|
Before Width: | Height: | Size: 6.9 KiB |
@ -1,36 +0,0 @@
|
|||||||
@startmindmap
|
|
||||||
|
|
||||||
<style>
|
|
||||||
mindmapDiagram {
|
|
||||||
//FontColor white
|
|
||||||
.done {
|
|
||||||
BackgroundColor green
|
|
||||||
BackgroundColor lightgreen
|
|
||||||
//FontColor black
|
|
||||||
}
|
|
||||||
.unknown {
|
|
||||||
BackgroundColor #FFBBCC
|
|
||||||
BackgroundColor purple
|
|
||||||
FontColor white
|
|
||||||
}
|
|
||||||
.skip {
|
|
||||||
BackgroundColor #FFBBCC
|
|
||||||
BackgroundColor red
|
|
||||||
FontColor white
|
|
||||||
}
|
|
||||||
.info {
|
|
||||||
BackgroundColor blue
|
|
||||||
BackgroundColor lightblue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
;top to bottom direction
|
|
||||||
|
|
||||||
* Configuration per character <<done>>
|
|
||||||
** service.yaml <<done>>
|
|
||||||
***_ Untuk kredensial service yang di gunakan
|
|
||||||
** model.yaml <<done>>
|
|
||||||
***_ Untuk menetapkan model set default yang digunakan
|
|
||||||
|
|
||||||
@endmindmap
|
|
||||||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 46 KiB |
@ -59,9 +59,5 @@ mindmapDiagram {
|
|||||||
*** World
|
*** World
|
||||||
**** Building
|
**** Building
|
||||||
***** Room
|
***** Room
|
||||||
** Mute Session End (Per Character) <<done>>
|
|
||||||
***_ Tidak Menampilkan "*Session End*" saat session timeout
|
|
||||||
** Continuous Greet after session end (Per Character)
|
|
||||||
***_ Untuk reset history di chat
|
|
||||||
|
|
||||||
@endmindmap
|
@endmindmap
|
||||||
|
Before Width: | Height: | Size: 15 KiB |
@ -1,42 +0,0 @@
|
|||||||
@startmindmap
|
|
||||||
|
|
||||||
<style>
|
|
||||||
mindmapDiagram {
|
|
||||||
//FontColor white
|
|
||||||
.done {
|
|
||||||
BackgroundColor green
|
|
||||||
BackgroundColor lightgreen
|
|
||||||
//FontColor black
|
|
||||||
}
|
|
||||||
.unknown {
|
|
||||||
BackgroundColor #FFBBCC
|
|
||||||
BackgroundColor purple
|
|
||||||
FontColor white
|
|
||||||
}
|
|
||||||
.skip {
|
|
||||||
BackgroundColor #FFBBCC
|
|
||||||
BackgroundColor red
|
|
||||||
FontColor white
|
|
||||||
}
|
|
||||||
.info {
|
|
||||||
BackgroundColor blue
|
|
||||||
BackgroundColor lightblue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
;top to bottom direction
|
|
||||||
|
|
||||||
* Tools for configuration
|
|
||||||
** Workspace Switch <<done>>
|
|
||||||
** Service toggler
|
|
||||||
** Listing model set and provider
|
|
||||||
** Model set switch <<skip>>
|
|
||||||
***_ Sepertinya mustahil dan potensi ngerusak sistem
|
|
||||||
|
|
||||||
* Tools for generation
|
|
||||||
** plantuml generator
|
|
||||||
** Slide deck
|
|
||||||
** Motion graphic
|
|
||||||
|
|
||||||
@endmindmap
|
|
||||||
BIN
plan/milestone/catatan-kertas.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
76
plan/milestone/catatan-kertas.puml
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
@startmindmap
|
||||||
|
|
||||||
|
<style>
|
||||||
|
mindmapDiagram {
|
||||||
|
//FontColor white
|
||||||
|
.done {
|
||||||
|
BackgroundColor green
|
||||||
|
BackgroundColor lightgreen
|
||||||
|
//FontColor black
|
||||||
|
}
|
||||||
|
.unknown {
|
||||||
|
BackgroundColor #FFBBCC
|
||||||
|
BackgroundColor purple
|
||||||
|
FontColor white
|
||||||
|
}
|
||||||
|
.skip {
|
||||||
|
BackgroundColor #FFBBCC
|
||||||
|
BackgroundColor red
|
||||||
|
FontColor white
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
BackgroundColor blue
|
||||||
|
BackgroundColor lightblue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
;top to bottom direction
|
||||||
|
|
||||||
|
* Tools for configuration
|
||||||
|
** Workspace Switch <<done>>
|
||||||
|
** Service toggler
|
||||||
|
** Listing model set and provider
|
||||||
|
** Model set switch <<skip>>
|
||||||
|
***_ Sepertinya mustahil dan potensi ngerusak sistem
|
||||||
|
|
||||||
|
* Tools for generation
|
||||||
|
** plantuml generator
|
||||||
|
** Slide deck
|
||||||
|
** Motion graphic
|
||||||
|
|
||||||
|
* TUI/GUI/API Settings
|
||||||
|
** TUI
|
||||||
|
*** Parameters for tokenizer
|
||||||
|
*** Service list
|
||||||
|
****_ Kontak dengan daemon
|
||||||
|
*** Model set & Provider list <<done>>
|
||||||
|
*** Roleplayer user ID
|
||||||
|
|
||||||
|
* Configuration per character <<done>>
|
||||||
|
** service.yaml <<done>>
|
||||||
|
***_ Untuk kredensial service yang di gunakan
|
||||||
|
** model.yaml <<done>>
|
||||||
|
***_ Untuk menetapkan model set default yang digunakan
|
||||||
|
|
||||||
|
* opencode main skill
|
||||||
|
** Explore (Passive)
|
||||||
|
** Plan
|
||||||
|
** Build
|
||||||
|
|
||||||
|
* Verbosity
|
||||||
|
** Concise
|
||||||
|
***_ Keep your answers short and to the point.
|
||||||
|
** Balanced
|
||||||
|
***_ Provide balanced answers. Not too brief, not too long.
|
||||||
|
** Detailed
|
||||||
|
***_ Give thorough, detailed answers with explanations.
|
||||||
|
|
||||||
|
* Full-Duplex Model
|
||||||
|
**_ Voice Activity Detection
|
||||||
|
**_ Noise interruption detector included
|
||||||
|
** Mic & Voice Frontend
|
||||||
|
** Models
|
||||||
|
*** GPT-Live-1
|
||||||
|
|
||||||
|
@endmindmap
|
||||||
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 148 KiB |
@ -27,43 +27,6 @@ mindmapDiagram {
|
|||||||
|
|
||||||
;top to bottom direction
|
;top to bottom direction
|
||||||
|
|
||||||
* Decision/Classification Features
|
|
||||||
** API Inference
|
|
||||||
*** Library
|
|
||||||
****_ Dependency
|
|
||||||
*****_ .venv/bin/pip install huggingface_hub transformers
|
|
||||||
****_ Download
|
|
||||||
*****_ .venv/bin/hf download convaiinnovations/laya --local-dir models/convaiinnovations/laya
|
|
||||||
*****_ .venv/bin/hf download convaiinnovations/laya-multilingual --local-dir models/convaiinnovations/laya-multilingual
|
|
||||||
*** Model
|
|
||||||
**** Classification
|
|
||||||
***** convaiinnovations/laya
|
|
||||||
***** convaiinnovations/laya-multilingual
|
|
||||||
*** Inference
|
|
||||||
**** llama.cpp
|
|
||||||
**** laya.cpp (lkarlslund)
|
|
||||||
*****_ HTTP Server bawaan
|
|
||||||
|
|
||||||
* opencode main skill
|
|
||||||
** Explore (Passive)
|
|
||||||
** Plan
|
|
||||||
** Build
|
|
||||||
|
|
||||||
* Verbosity
|
|
||||||
** Concise
|
|
||||||
***_ Keep your answers short and to the point.
|
|
||||||
** Balanced
|
|
||||||
***_ Provide balanced answers. Not too brief, not too long.
|
|
||||||
** Detailed
|
|
||||||
***_ Give thorough, detailed answers with explanations.
|
|
||||||
|
|
||||||
* Full-Duplex Model
|
|
||||||
**_ Voice Activity Detection
|
|
||||||
**_ Noise interruption detector included
|
|
||||||
** Mic & Voice Frontend
|
|
||||||
** Models
|
|
||||||
*** GPT-Live-1
|
|
||||||
|
|
||||||
* Speech
|
* Speech
|
||||||
** Push to talk
|
** Push to talk
|
||||||
*** STT (ASR)
|
*** STT (ASR)
|
||||||
|
|||||||
@ -4,7 +4,18 @@ import re
|
|||||||
import fnmatch
|
import fnmatch
|
||||||
import glob as glob_module
|
import glob as glob_module
|
||||||
|
|
||||||
from lib import workspace
|
# Global state to track the agent's current working directory
|
||||||
|
# This ensures that subsequent bash commands run in the correct directory
|
||||||
|
CURRENT_WORKSPACE = os.getcwd()
|
||||||
|
|
||||||
|
def get_current_workspace():
|
||||||
|
global CURRENT_WORKSPACE
|
||||||
|
return CURRENT_WORKSPACE
|
||||||
|
|
||||||
|
def set_current_workspace(path):
|
||||||
|
global CURRENT_WORKSPACE
|
||||||
|
CURRENT_WORKSPACE = path
|
||||||
|
os.chdir(path)
|
||||||
|
|
||||||
schema_read_file = {
|
schema_read_file = {
|
||||||
"type": "function",
|
"type": "function",
|
||||||
@ -23,8 +34,8 @@ schema_read_file = {
|
|||||||
|
|
||||||
def read_file(path):
|
def read_file(path):
|
||||||
try:
|
try:
|
||||||
# Use absolute path if it's relative to current workspace
|
# Use absolute path if it's relative to CURRENT_WORKSPACE
|
||||||
full_path = os.path.join(workspace.get_current_workspace(), path) if not os.path.isabs(path) else path
|
full_path = os.path.join(get_current_workspace(), path) if not os.path.isabs(path) else path
|
||||||
with open(full_path, 'r', encoding='utf-8', errors='replace') as f:
|
with open(full_path, 'r', encoding='utf-8', errors='replace') as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
return ''.join(f"{i+1}: {line}" for i, line in enumerate(lines))
|
return ''.join(f"{i+1}: {line}" for i, line in enumerate(lines))
|
||||||
@ -49,7 +60,7 @@ schema_write_file = {
|
|||||||
|
|
||||||
def write_file(path, content):
|
def write_file(path, content):
|
||||||
try:
|
try:
|
||||||
full_path = os.path.join(workspace.get_current_workspace(), path) if not os.path.isabs(path) else path
|
full_path = os.path.join(get_current_workspace(), path) if not os.path.isabs(path) else path
|
||||||
with open(full_path, 'w', encoding='utf-8') as f:
|
with open(full_path, 'w', encoding='utf-8') as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
return f"Successfully wrote to {full_path}"
|
return f"Successfully wrote to {full_path}"
|
||||||
@ -75,7 +86,7 @@ schema_edit_file = {
|
|||||||
|
|
||||||
def edit_file(path, old_string, new_string):
|
def edit_file(path, old_string, new_string):
|
||||||
try:
|
try:
|
||||||
full_path = os.path.join(workspace.get_current_workspace(), path) if not os.path.isabs(path) else path
|
full_path = os.path.join(get_current_workspace(), path) if not os.path.isabs(path) else path
|
||||||
with open(full_path, 'r', encoding='utf-8', errors='replace') as f:
|
with open(full_path, 'r', encoding='utf-8', errors='replace') as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
if old_string not in content:
|
if old_string not in content:
|
||||||
@ -105,7 +116,7 @@ schema_run_bash = {
|
|||||||
def run_bash(command):
|
def run_bash(command):
|
||||||
try:
|
try:
|
||||||
# Prepend 'cd' to the command to ensure it runs in the correct workspace
|
# Prepend 'cd' to the command to ensure it runs in the correct workspace
|
||||||
full_command = f"cd '{workspace.get_current_workspace()}' && {command}"
|
full_command = f"cd '{get_current_workspace()}' && {command}"
|
||||||
result = subprocess.run(full_command, shell=True, capture_output=True, text=True, timeout=30)
|
result = subprocess.run(full_command, shell=True, capture_output=True, text=True, timeout=30)
|
||||||
return f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}\nreturn code: {result.returncode}"
|
return f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}\nreturn code: {result.returncode}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -131,7 +142,7 @@ schema_search_code = {
|
|||||||
def search_code(pattern, search_type, path="."):
|
def search_code(pattern, search_type, path="."):
|
||||||
try:
|
try:
|
||||||
# Resolve path relative to workspace
|
# Resolve path relative to workspace
|
||||||
search_path = os.path.join(workspace.get_current_workspace(), path) if not os.path.isabs(path) else path
|
search_path = os.path.join(get_current_workspace(), path) if not os.path.isabs(path) else path
|
||||||
|
|
||||||
if search_type == "glob":
|
if search_type == "glob":
|
||||||
files = glob_module.glob(f"{search_path}/**/{pattern}", recursive=True)
|
files = glob_module.glob(f"{search_path}/**/{pattern}", recursive=True)
|
||||||
@ -176,11 +187,41 @@ def git_operation(args):
|
|||||||
try:
|
try:
|
||||||
# Execute git command from the current workspace
|
# Execute git command from the current workspace
|
||||||
full_command = ["git", *args]
|
full_command = ["git", *args]
|
||||||
result = subprocess.run(full_command, cwd=workspace.get_current_workspace(), capture_output=True, text=True, timeout=10)
|
result = subprocess.run(full_command, cwd=get_current_workspace(), capture_output=True, text=True, timeout=10)
|
||||||
return f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}\nreturn code: {result.returncode}"
|
return f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}\nreturn code: {result.returncode}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"Error running git command: {str(e)}"
|
return f"Error running git command: {str(e)}"
|
||||||
|
|
||||||
|
schema_workspace_change = {
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "workspace_change",
|
||||||
|
"description": "Change the current working directory of the agent session. Supports tilde (~) expansion for the user home directory (/home/aji/).",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": {"type": "string", "description": "The target directory path to change to."}
|
||||||
|
},
|
||||||
|
"required": ["path"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def workspace_change(path):
|
||||||
|
try:
|
||||||
|
if path.startswith("~"):
|
||||||
|
target_path = os.path.expanduser(path)
|
||||||
|
else:
|
||||||
|
target_path = os.path.abspath(path)
|
||||||
|
|
||||||
|
if os.path.exists(target_path) and os.path.isdir(target_path):
|
||||||
|
set_current_workspace(target_path)
|
||||||
|
return f"Successfully changed workspace to: {target_path}"
|
||||||
|
else:
|
||||||
|
return f"Error: The path {target_path} does not exist or is not a directory."
|
||||||
|
except Exception as e:
|
||||||
|
return f"Error changing workspace: {str(e)}"
|
||||||
|
|
||||||
schema_search_grep = {
|
schema_search_grep = {
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
@ -200,7 +241,7 @@ schema_search_grep = {
|
|||||||
|
|
||||||
def search_grep(pattern, path=".", include=None):
|
def search_grep(pattern, path=".", include=None):
|
||||||
try:
|
try:
|
||||||
search_path = os.path.join(workspace.get_current_workspace(), path) if not os.path.isabs(path) else path
|
search_path = os.path.join(get_current_workspace(), path) if not os.path.isabs(path) else path
|
||||||
results = []
|
results = []
|
||||||
for root, dirs, files in os.walk(search_path):
|
for root, dirs, files in os.walk(search_path):
|
||||||
for file in files:
|
for file in files:
|
||||||
@ -236,7 +277,7 @@ schema_search_glob = {
|
|||||||
|
|
||||||
def search_glob(pattern, path="."):
|
def search_glob(pattern, path="."):
|
||||||
try:
|
try:
|
||||||
search_path = os.path.join(workspace.get_current_workspace(), path) if not os.path.isabs(path) else path
|
search_path = os.path.join(get_current_workspace(), path) if not os.path.isabs(path) else path
|
||||||
files = glob_module.glob(f"{search_path}/{pattern}", recursive=True)
|
files = glob_module.glob(f"{search_path}/{pattern}", recursive=True)
|
||||||
def _isfile(f):
|
def _isfile(f):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -1,49 +0,0 @@
|
|||||||
import os
|
|
||||||
from lib import workspace
|
|
||||||
|
|
||||||
# Tools untuk membantu sistem Hendrik.
|
|
||||||
# State workspace sesi dimiliki lib.workspace dan dipakai bersama oleh
|
|
||||||
# tool coding, vision, dan TUI -- di sini hanya dibungkus sebagai tool aksi.
|
|
||||||
|
|
||||||
schema_workspace_change = {
|
|
||||||
"type": "function",
|
|
||||||
"function": {
|
|
||||||
"name": "workspace_change",
|
|
||||||
"description": "Change the current working directory of the agent session. Supports tilde (~) expansion for the user home directory (/home/aji/).",
|
|
||||||
"parameters": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"path": {"type": "string", "description": "The target directory path to change to."}
|
|
||||||
},
|
|
||||||
"required": ["path"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def workspace_change(path):
|
|
||||||
previous_path = workspace.get_current_workspace()
|
|
||||||
try:
|
|
||||||
if path.startswith("~"):
|
|
||||||
target_path = os.path.expanduser(path)
|
|
||||||
else:
|
|
||||||
target_path = os.path.abspath(path)
|
|
||||||
|
|
||||||
if os.path.exists(target_path) and os.path.isdir(target_path):
|
|
||||||
workspace.set_current_workspace(target_path)
|
|
||||||
return (
|
|
||||||
f"Successfully changed workspace.\n"
|
|
||||||
f"previous workspace: {previous_path}\n"
|
|
||||||
f"current workspace : {target_path}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return (
|
|
||||||
f"Error: The path {target_path} does not exist or is not a directory.\n"
|
|
||||||
f"previous workspace: {previous_path}\n"
|
|
||||||
f"current workspace : {previous_path} (unchanged)"
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
return (
|
|
||||||
f"Error changing workspace: {str(e)}\n"
|
|
||||||
f"previous workspace: {previous_path}\n"
|
|
||||||
f"current workspace : {workspace.get_current_workspace()}"
|
|
||||||
)
|
|
||||||
@ -6,7 +6,7 @@ import tempfile
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
import ftplib
|
import ftplib
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from lib import workspace
|
from tools.coder import get_current_workspace
|
||||||
|
|
||||||
MIME_MAP = {
|
MIME_MAP = {
|
||||||
".jpg": "image/jpeg",
|
".jpg": "image/jpeg",
|
||||||
@ -152,7 +152,7 @@ def describe_image(path):
|
|||||||
return f"Error fetching SFTP: {e}"
|
return f"Error fetching SFTP: {e}"
|
||||||
|
|
||||||
# ── Local file ─────────────────────────────────────────────────────
|
# ── Local file ─────────────────────────────────────────────────────
|
||||||
full_path = os.path.join(workspace.get_current_workspace(), path) if not os.path.isabs(path) else path
|
full_path = os.path.join(get_current_workspace(), path) if not os.path.isabs(path) else path
|
||||||
|
|
||||||
if not os.path.isfile(full_path):
|
if not os.path.isfile(full_path):
|
||||||
return f"Error: file not found: {full_path}"
|
return f"Error: file not found: {full_path}"
|
||||||
|
|||||||