From 1048bc145cd0341fcbdd269f3275079bc40ede70 Mon Sep 17 00:00:00 2001 From: batsumaru <> Date: Fri, 11 Sep 2026 13:56:07 +0900 Subject: Added llm logic and config --- agent.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ agent.toml | 11 +++++++++++ client.py | 48 ++++++++++++++++++------------------------------ context.py | 24 ++++++++++++++++++++++++ identity.py | 34 ++++++++++++++++++++++++++++++++++ llm.py | 42 ++++++++++++++++++++++++++++++++++++++++++ user.toml | 3 +++ 7 files changed, 177 insertions(+), 30 deletions(-) create mode 100644 agent.py create mode 100644 agent.toml create mode 100644 context.py create mode 100644 identity.py create mode 100644 llm.py create mode 100644 user.toml diff --git a/agent.py b/agent.py new file mode 100644 index 0000000..51661fd --- /dev/null +++ b/agent.py @@ -0,0 +1,45 @@ +from collections.abc import Iterator + +from context import ContextBuilder +from identity import AgentIdentity, UserProfile +from llm import LLM + +class Agent: + def __init__( + self, + llm: LLM, + agent_identity: AgentIdentity, + user_profile: UserProfile + ) -> None: + self.llm = llm + + self.context = ContextBuilder( + agent_identity, + user_profile, + ) + + self.messages: list[dict[str, str]] = [ + { + "role": "system", + "content": self.context.build_system_prompt(), + } + ] + + def chat(self, user_input: str) -> Iterator[str]: + self.messages.append({ + "role": "user", + "content": user_input, + }) + + answer_parts: list[str] = [] + + for chunk in self.llm.chat(self.messages): + yield chunk + answer_parts.append(chunk) + + answer = "".join(answer_parts) + + self.messages.append({ + "role": "assistant", + "content": answer, + }) diff --git a/agent.toml b/agent.toml new file mode 100644 index 0000000..87189cd --- /dev/null +++ b/agent.toml @@ -0,0 +1,11 @@ +name = "Personal Agent" + +[personality] +style = "casual, helpful, concise, conversational" + +[values] +honesty = true +directness = true + +[quirks] +favorite_color = "purple and orange" diff --git a/client.py b/client.py index 0974650..c983db1 100644 --- a/client.py +++ b/client.py @@ -1,8 +1,17 @@ -import requests +from agent import Agent +from identity import load_agent_identity, load_user_profile +from llm import LLM -URL = "http://localhost:8080/v1/chat/completions" +llm = LLM("http://localhost:8080/v1/chat/completions",) -messages = [] +agent_identity = load_agent_identity("agent.toml") +user_profile = load_user_profile("user.toml") + +agent = Agent( + llm, + agent_identity, + user_profile, +) while True: user_input = input("You: ") @@ -10,30 +19,9 @@ while True: if user_input.lower() in {"exit", "quit"}: break - messages.append({ - "role": "user", - "content": user_input - }) - - response = requests.post( - URL, - json={ - "messages": messages, - "max_tokens": 256, - "temperature": 0.7, - } - ) - - if not response.ok: - print("Server error: ", response.text) - continue - - data = response.json() - answer = data["choices"][0]["message"]["content"] - - print(f"Agent: {answer}") - - messages.append({ - "role": "assistant", - "content": answer - }) + print("Agent: ", end="", flush=True) + + for chunk in agent.chat(user_input): + print(chunk, end="", flush=True) + + print() diff --git a/context.py b/context.py new file mode 100644 index 0000000..5006e3a --- /dev/null +++ b/context.py @@ -0,0 +1,24 @@ +from identity import AgentIdentity, UserProfile + +class ContextBuilder: + def __init__( + self, + agent_identity: AgentIdentity, + user_profile: UserProfile, + ) -> None: + self.agent_identity = agent_identity + self.user_profile = user_profile + + def build_system_prompt(self) -> str: + agent = self.agent_identity + user = self.user_profile + + return ( + f"You are {agent.name}, a personal AI assistant.\n" + f"Personality: {agent.personality['style']}.\n" + f"Honesty: {agent.values['honesty']}.\n" + f"Directness: {agent.values['directness']}.\n" + f"Favorite color: {agent.quirks['favorite_color']}.\n" + f"\n" + f"The user's name is {user.name}" + ) diff --git a/identity.py b/identity.py new file mode 100644 index 0000000..9b4faa0 --- /dev/null +++ b/identity.py @@ -0,0 +1,34 @@ +from dataclasses import dataclass +import tomllib + +@dataclass +class AgentIdentity: + name: str + personality: dict[str, str] + values: dict[str, bool] + quirks: dict[str, str] + +@dataclass +class UserProfile: + name: str + preferences: dict[str, str] + +def load_agent_identity(path: str) -> AgentIdentity: + with open(path, "rb") as file: + data = tomllib.load(file) + + return AgentIdentity( + name=data["name"], + personality=data["personality"], + values=data["values"], + quirks=data["quirks"], + ) + +def load_user_profile(path: str) -> UserProfile: + with open(path, "rb") as file: + data = tomllib.load(file) + + return UserProfile( + name=data["name"], + preferences=data["preferences"], + ) diff --git a/llm.py b/llm.py new file mode 100644 index 0000000..524f173 --- /dev/null +++ b/llm.py @@ -0,0 +1,42 @@ +import json +from collections.abc import Iterator + +import requests + +class LLM: + def __init__(self, url: str) -> None: + self.url = url + + def chat(self, messages: list[dict[str, str]], max_tokens: int = 256) -> Iterator[str]: + response = requests.post( + self.url, + json = { + "messages": messages, + "max_tokens": max_tokens, + "temperature": 0.7, + "stream": True, + }, + stream=True, + ) + response.raise_for_status() + + for line in response.iter_lines(): + if not line: + continue + + if not line.startswith(b"data: "): + continue + + data = line[6:] + + if data == b"[DONE]": + break + + chunk = json.loads(data) + + delta = chunk["choices"][0]["delta"] + content = delta.get("content") + + if content: + yield content + diff --git a/user.toml b/user.toml new file mode 100644 index 0000000..fae9d85 --- /dev/null +++ b/user.toml @@ -0,0 +1,3 @@ +name = "User" + +[preferences] -- cgit v1.3