Character script
This commit is contained in:
parent
432a8b2059
commit
9643b95059
23
.env.example
23
.env.example
@ -24,14 +24,21 @@ AGENT_MAX_ITERATIONS=20
|
|||||||
AGENT_MAX_TOOL_OUTPUT=4000
|
AGENT_MAX_TOOL_OUTPUT=4000
|
||||||
|
|
||||||
# Personality
|
# Personality
|
||||||
PERSONA_MODE=programmer # Mode AI: "programmer" (default, koding) | "roleplayer" (ngobrol)
|
# Character preset — baca dari directory character/<AGENT_CHARACTER>
|
||||||
PERSONA_NAME=OWL
|
# Kosongkan untuk memakai PERSONA_* dari .env.
|
||||||
PERSONA_TONE=casual # casual | formal | playful | warm
|
# Contoh: AGENT_CHARACTER=hendrik
|
||||||
PERSONA_VERBOSITY=balanced # concise | balanced | detailed
|
AGENT_CHARACTER=hendrik
|
||||||
PERSONA_HUMOR=light # none | light | witty
|
|
||||||
PERSONA_LANGUAGE=id # id | en | (kosong = auto)
|
#PERSONA_MODE=programmer # Mode AI: "programmer" (default, koding) | "roleplayer" (ngobrol)
|
||||||
PERSONA_MOOD=cheerful # cheerful | calm | energetic | sarcastic
|
#PERSONA_NAME=OWL
|
||||||
PERSONA_CATCHPHRASES=Hai, Gimana kabarnya?, Wkwkwk
|
#PERSONA_AGE= # Opsional, contoh: 24
|
||||||
|
#PERSONA_GENDER= # Opsional, contoh: male | female | non-binary
|
||||||
|
#PERSONA_TONE=casual # casual | formal | playful | warm
|
||||||
|
#PERSONA_VERBOSITY=balanced # concise | balanced | detailed
|
||||||
|
#PERSONA_HUMOR=light # none | light | witty
|
||||||
|
#PERSONA_LANGUAGE=id # id | en | (kosong = auto)
|
||||||
|
#PERSONA_MOOD=cheerful # cheerful | calm | energetic | sarcastic
|
||||||
|
#PERSONA_CATCHPHRASES=Hai, Gimana kabarnya?, Wkwkwk
|
||||||
|
|
||||||
XMPP_ENABLED=False
|
XMPP_ENABLED=False
|
||||||
|
|
||||||
|
|||||||
9
character/hendrik
Normal file
9
character/hendrik
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
PERSONA_MODE=programmer
|
||||||
|
PERSONA_NAME=Hendrik
|
||||||
|
PERSONA_AGE=35
|
||||||
|
PERSONA_GENDER=male
|
||||||
|
PERSONA_TONE=formal
|
||||||
|
PERSONA_VERBOSITY=concise
|
||||||
|
PERSONA_HUMOR=none
|
||||||
|
PERSONA_LANGUAGE=id
|
||||||
|
PERSONA_MOOD=calm
|
||||||
45
config.py
45
config.py
@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
@ -29,6 +30,12 @@ PERSONA_MODE = os.getenv("PERSONA_MODE", default="programmer").strip().lower()
|
|||||||
# Personality — nama panggilan AI (default: "OWL")
|
# Personality — nama panggilan AI (default: "OWL")
|
||||||
PERSONA_NAME = os.getenv("PERSONA_NAME", default="OWL").strip() or "OWL"
|
PERSONA_NAME = os.getenv("PERSONA_NAME", default="OWL").strip() or "OWL"
|
||||||
|
|
||||||
|
# Persona age (optional)
|
||||||
|
PERSONA_AGE = os.getenv("PERSONA_AGE", default="").strip()
|
||||||
|
|
||||||
|
# Persona gender (optional)
|
||||||
|
PERSONA_GENDER = os.getenv("PERSONA_GENDER", default="").strip()
|
||||||
|
|
||||||
# Gaya bicara: "casual" | "formal" | "playful" | "warm"
|
# Gaya bicara: "casual" | "formal" | "playful" | "warm"
|
||||||
PERSONA_TONE = os.getenv("PERSONA_TONE", default="casual").strip().lower() or "casual"
|
PERSONA_TONE = os.getenv("PERSONA_TONE", default="casual").strip().lower() or "casual"
|
||||||
|
|
||||||
@ -48,6 +55,44 @@ PERSONA_MOOD = os.getenv("PERSONA_MOOD", default="cheerful").strip().lower() or
|
|||||||
# Contoh: "Siap bro!, Haha~, Wkwkwk"
|
# Contoh: "Siap bro!, Haha~, Wkwkwk"
|
||||||
PERSONA_CATCHPHRASES = os.getenv("PERSONA_CATCHPHRASES", default="").strip()
|
PERSONA_CATCHPHRASES = os.getenv("PERSONA_CATCHPHRASES", default="").strip()
|
||||||
|
|
||||||
|
# Character preset — baca dari directory character/<AGENT_CHARACTER>
|
||||||
|
CHARACTER_DIR = Path(__file__).resolve().parent / "character"
|
||||||
|
AGENT_CHARACTER = os.getenv("AGENT_CHARACTER", default="").strip().lower()
|
||||||
|
CHARACTER_CONFIG_PATH = CHARACTER_DIR / AGENT_CHARACTER if AGENT_CHARACTER else None
|
||||||
|
if CHARACTER_CONFIG_PATH and CHARACTER_CONFIG_PATH.is_file():
|
||||||
|
_character_env = {}
|
||||||
|
for line in CHARACTER_CONFIG_PATH.read_text(encoding="utf-8").splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
if "=" in line:
|
||||||
|
key, value = line.split("=", 1)
|
||||||
|
_character_env[key.strip()] = value.strip()
|
||||||
|
|
||||||
|
_character_overrides = {
|
||||||
|
"PERSONA_MODE": PERSONA_MODE,
|
||||||
|
"PERSONA_NAME": PERSONA_NAME,
|
||||||
|
"PERSONA_AGE": PERSONA_AGE,
|
||||||
|
"PERSONA_GENDER": PERSONA_GENDER,
|
||||||
|
"PERSONA_TONE": PERSONA_TONE,
|
||||||
|
"PERSONA_VERBOSITY": PERSONA_VERBOSITY,
|
||||||
|
"PERSONA_HUMOR": PERSONA_HUMOR,
|
||||||
|
"PERSONA_LANGUAGE": PERSONA_LANGUAGE,
|
||||||
|
"PERSONA_MOOD": PERSONA_MOOD,
|
||||||
|
"PERSONA_CATCHPHRASES": PERSONA_CATCHPHRASES,
|
||||||
|
}
|
||||||
|
for key, fallback in _character_overrides.items():
|
||||||
|
value = _character_env.get(key, fallback).strip()
|
||||||
|
if key in {"PERSONA_MODE", "PERSONA_TONE", "PERSONA_VERBOSITY", "PERSONA_HUMOR", "PERSONA_LANGUAGE", "PERSONA_MOOD"}:
|
||||||
|
value = value.lower() or fallback
|
||||||
|
if key == "PERSONA_NAME" and not value:
|
||||||
|
value = fallback
|
||||||
|
_character_overrides[key] = value
|
||||||
|
|
||||||
|
for key, value in _character_overrides.items():
|
||||||
|
globals()[key] = value
|
||||||
|
os.environ[key] = value
|
||||||
|
|
||||||
# Selective response: true = roleplayer hanya respon kalau ada mention/relevansi (default).
|
# Selective response: true = roleplayer hanya respon kalau ada mention/relevansi (default).
|
||||||
# false = roleplayer semua pesan ikut respon (seperti biasa, tanpa filter).
|
# false = roleplayer semua pesan ikut respon (seperti biasa, tanpa filter).
|
||||||
XMPP_SELECTIVE_RESPONSE = os.getenv("XMPP_SELECTIVE_RESPONSE", default="true").strip().lower() in ("true", "1", "yes")
|
XMPP_SELECTIVE_RESPONSE = os.getenv("XMPP_SELECTIVE_RESPONSE", default="true").strip().lower() in ("true", "1", "yes")
|
||||||
|
|||||||
@ -36,15 +36,21 @@ class LLMClient:
|
|||||||
message = response['choices'][0]['message']
|
message = response['choices'][0]['message']
|
||||||
return self.Message(message)
|
return self.Message(message)
|
||||||
except urllib.error.HTTPError as e:
|
except urllib.error.HTTPError as e:
|
||||||
|
body_text = ""
|
||||||
|
try:
|
||||||
|
body_text = e.read().decode('utf-8', errors='replace')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
if tools and e.code == 404:
|
if tools and e.code == 404:
|
||||||
try:
|
try:
|
||||||
body = json.loads(e.read().decode('utf-8'))
|
body = json.loads(body_text) if body_text else {}
|
||||||
if 'tool use' in body.get('error', {}).get('message', '').lower():
|
if 'tool use' in body.get('error', {}).get('message', '').lower():
|
||||||
result = self.chat(messages, tools=None)
|
result = self.chat(messages, tools=None)
|
||||||
result.warning = "Tool calling not supported by this model. Running in chat-only mode."
|
result.warning = "Tool calling not supported by this model. Running in chat-only mode."
|
||||||
return result
|
return result
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return self.Message({'content': f"HTTP Error: {e.code} {e.reason}", 'tool_calls': None})
|
detail = f" - {body_text[:500]}" if body_text else ""
|
||||||
|
return self.Message({'content': f"HTTP Error: {e.code} {e.reason}{detail}", 'tool_calls': None})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.Message({'content': f"Error: {str(e)}", 'tool_calls': None})
|
return self.Message({'content': f"Error: {str(e)}", 'tool_calls': None})
|
||||||
|
|||||||
@ -20,6 +20,12 @@ class PersonalityConfig:
|
|||||||
# Nama panggilan AI (default: "OWL")
|
# Nama panggilan AI (default: "OWL")
|
||||||
name: str = "OWL"
|
name: str = "OWL"
|
||||||
|
|
||||||
|
# Umur persona AI (opsional; kosong bila tidak ingin ditampilkan)
|
||||||
|
age: str = ""
|
||||||
|
|
||||||
|
# Jenis kelamin persona AI (opsional; kosong bila tidak ingin ditampilkan)
|
||||||
|
gender: str = ""
|
||||||
|
|
||||||
# Gaya bicara:
|
# Gaya bicara:
|
||||||
# "casual" → santai, gaul
|
# "casual" → santai, gaul
|
||||||
# "formal" → sopan, profesional
|
# "formal" → sopan, profesional
|
||||||
@ -64,6 +70,8 @@ def _load_personality_from_env() -> PersonalityConfig:
|
|||||||
|
|
||||||
return PersonalityConfig(
|
return PersonalityConfig(
|
||||||
name=os.getenv("PERSONA_NAME", default="OWL").strip() or "OWL",
|
name=os.getenv("PERSONA_NAME", default="OWL").strip() or "OWL",
|
||||||
|
age=os.getenv("PERSONA_AGE", default="").strip(),
|
||||||
|
gender=os.getenv("PERSONA_GENDER", default="").strip(),
|
||||||
tone=os.getenv("PERSONA_TONE", default="casual").strip().lower() or "casual",
|
tone=os.getenv("PERSONA_TONE", default="casual").strip().lower() or "casual",
|
||||||
verbosity=os.getenv("PERSONA_VERBOSITY", default="balanced").strip().lower() or "balanced",
|
verbosity=os.getenv("PERSONA_VERBOSITY", default="balanced").strip().lower() or "balanced",
|
||||||
humor_level=os.getenv("PERSONA_HUMOR", default="light").strip().lower() or "light",
|
humor_level=os.getenv("PERSONA_HUMOR", default="light").strip().lower() or "light",
|
||||||
@ -83,6 +91,12 @@ def _build_personality_block(cfg: PersonalityConfig) -> str:
|
|||||||
"""Generate deskripsi personality dari config."""
|
"""Generate deskripsi personality dari config."""
|
||||||
parts = [f"You are {cfg.name}."]
|
parts = [f"You are {cfg.name}."]
|
||||||
|
|
||||||
|
if cfg.age:
|
||||||
|
parts.append(f"Your persona age is {cfg.age} years old.")
|
||||||
|
|
||||||
|
if cfg.gender:
|
||||||
|
parts.append(f"Your persona gender is {cfg.gender}.")
|
||||||
|
|
||||||
# Tone
|
# Tone
|
||||||
tone_map = {
|
tone_map = {
|
||||||
"casual": "You speak in a casual, relaxed manner — like chatting with a friend.",
|
"casual": "You speak in a casual, relaxed manner — like chatting with a friend.",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user