diff --git a/pyproject.toml b/pyproject.toml
index 6be68ee9..2ee3bb02 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
[tool.poetry]
name = "talemate"
-version = "0.8.0"
+version = "0.9.0"
description = "AI-backed roleplay and narrative tools"
authors = ["FinalWombat"]
license = "GNU Affero General Public License v3.0"
diff --git a/src/talemate/__init__.py b/src/talemate/__init__.py
index e76aa49c..ab2e0f3f 100644
--- a/src/talemate/__init__.py
+++ b/src/talemate/__init__.py
@@ -2,4 +2,4 @@ from .agents import Agent
from .client import TextGeneratorWebuiClient
from .tale_mate import *
-VERSION = "0.8.0"
\ No newline at end of file
+VERSION = "0.9.0"
\ No newline at end of file
diff --git a/src/talemate/agents/base.py b/src/talemate/agents/base.py
index 3513eff7..b847d809 100644
--- a/src/talemate/agents/base.py
+++ b/src/talemate/agents/base.py
@@ -12,6 +12,32 @@ import talemate.util as util
from talemate.emit import emit
+__all__ = [
+ "Agent",
+ "set_processing",
+]
+
+def set_processing(fn):
+ """
+ decorator that emits the agent status as processing while the function
+ is running.
+
+ Done via a try - final block to ensure the status is reset even if
+ the function fails.
+ """
+
+ async def wrapper(self, *args, **kwargs):
+ try:
+ await self.emit_status(processing=True)
+ return await fn(self, *args, **kwargs)
+ finally:
+ await self.emit_status(processing=False)
+
+ wrapper.__name__ = fn.__name__
+
+ return wrapper
+
+
class Agent(ABC):
"""
Base agent class, defines a role
@@ -19,6 +45,8 @@ class Agent(ABC):
agent_type = "agent"
verbose_name = None
+
+ set_processing = set_processing
@property
def agent_details(self):
@@ -51,16 +79,29 @@ class Agent(ABC):
@property
def status(self):
if self.ready:
- return "idle"
+ return "idle" if getattr(self, "processing", 0) == 0 else "busy"
else:
return "uninitialized"
async def emit_status(self, processing: bool = None):
- if processing is not None:
- self.processing = processing
-
- status = "busy" if getattr(self, "processing", False) else self.status
-
+
+ # should keep a count of processing requests, and when the
+ # number is 0 status is "idle", if the number is greater than 0
+ # status is "busy"
+ #
+ # increase / decrease based on value of `processing`
+
+ if getattr(self, "processing", None) is None:
+ self.processing = 0
+
+ if not processing:
+ self.processing -= 1
+ self.processing = max(0, self.processing)
+ else:
+ self.processing += 1
+
+ status = "busy" if self.processing > 0 else "idle"
+
emit(
"agent_status",
message=self.verbose_name or "",
diff --git a/src/talemate/agents/conversation.py b/src/talemate/agents/conversation.py
index 3a8be48b..05de59b2 100644
--- a/src/talemate/agents/conversation.py
+++ b/src/talemate/agents/conversation.py
@@ -11,7 +11,7 @@ from talemate.emit import emit
from talemate.scene_message import CharacterMessage, DirectorMessage
from talemate.prompts import Prompt
-from .base import Agent
+from .base import Agent, set_processing
from .registry import register
if TYPE_CHECKING:
@@ -29,6 +29,8 @@ class ConversationAgent(Agent):
agent_type = "conversation"
verbose_name = "Conversation"
+
+ min_dialogue_length = 75
def __init__(
self,
@@ -162,24 +164,17 @@ class ConversationAgent(Agent):
if "#" in result:
result = result.split("#")[0]
- result = result.replace("\n", " ").strip()
-
-
- # Check for occurrence of a character name followed by a colon
- # that does NOT match the character name of the current character
- if "." in result and re.search(rf"(?!{self.character.name})\w+:", result):
- result = re.sub(rf"(?!{character.name})\w+:(.*\n*)*", "", result)
-
+ result = result.replace("\n", "__LINEBREAK__").strip()
# Removes partial sentence at the end
result = re.sub(r"[^\.\?\!\*]+(\n|$)", "", result)
-
result = result.replace(" :", ":")
-
result = result.strip().strip('"').strip()
-
+ result = result.replace("[", "*").replace("]", "*")
result = result.replace("**", "*")
+
+ result = result.replace("__LINEBREAK__", "\n")
# if there is an uneven number of '*' add one to the end
@@ -188,13 +183,12 @@ class ConversationAgent(Agent):
return result
+ @set_processing
async def converse(self, actor, editor=None):
"""
Have a conversation with the AI
"""
- await self.emit_status(processing=True)
-
history = actor.history
self.current_memory_context = None
@@ -212,7 +206,7 @@ class ConversationAgent(Agent):
empty_result_count = 0
# Validate AI response
- while loop_count < max_loops:
+ while loop_count < max_loops and len(total_result) < self.min_dialogue_length:
log.debug("conversation agent", result=result)
result = await self.client.send_prompt(
await self.build_prompt(character, char_message=total_result)
@@ -227,7 +221,7 @@ class ConversationAgent(Agent):
loop_count += 1
- if len(total_result) >= 250:
+ if len(total_result) > self.min_dialogue_length:
break
# if result is empty, increment empty_result_count
@@ -240,9 +234,6 @@ class ConversationAgent(Agent):
result = result.replace(" :", ":")
- # Removes any line starting with another character name followed by a colon
- total_result = re.sub(rf"(?!{character.name})\w+:(.*\n*)*", "", total_result)
-
total_result = total_result.split("#")[0]
# Removes partial sentence at the end
@@ -277,6 +268,4 @@ class ConversationAgent(Agent):
# Add message and response to conversation history
actor.scene.push_history(messages)
- await self.emit_status(processing=False)
-
return messages
diff --git a/src/talemate/agents/director.py b/src/talemate/agents/director.py
index deeb5fd9..23e34b5d 100644
--- a/src/talemate/agents/director.py
+++ b/src/talemate/agents/director.py
@@ -14,6 +14,7 @@ from talemate.automated_action import AutomatedAction
import talemate.automated_action as automated_action
from .conversation import ConversationAgent
from .registry import register
+from .base import set_processing
if TYPE_CHECKING:
from talemate import Actor, Character, Player, Scene
@@ -68,37 +69,34 @@ class DirectorAgent(ConversationAgent):
log.info("question_direction", response=response)
return response, evaluation, prompt
-
+ @set_processing
async def direct(self, character: Character, goal_override:str=None):
- await self.emit_status(processing=True)
analysis, current_goal, action = await self.decide_action(character, goal_override=goal_override)
- try:
- if action == "watch":
- return None
+ if action == "watch":
+ return None
+
+ if action == "direct":
+ return await self.direct_character_with_self_reflection(character, analysis, goal_override=current_goal)
+
+ if action.startswith("narrate"):
- if action == "direct":
- return await self.direct_character_with_self_reflection(character, analysis, goal_override=current_goal)
+ narration_type = action.split(":")[1]
- if action.startswith("narrate"):
-
- narration_type = action.split(":")[1]
-
- direct_narrative = await self.direct_narrative(analysis, narration_type=narration_type, goal=current_goal)
- if direct_narrative:
- narrator = self.scene.get_helper("narrator").agent
- narrator_response = await narrator.progress_story(direct_narrative)
- if not narrator_response:
- return None
- narrator_message = NarratorMessage(narrator_response, source="progress_story")
- self.scene.push_history(narrator_message)
- emit("narrator", narrator_message)
- return True
- finally:
- await self.emit_status(processing=False)
+ direct_narrative = await self.direct_narrative(analysis, narration_type=narration_type, goal=current_goal)
+ if direct_narrative:
+ narrator = self.scene.get_helper("narrator").agent
+ narrator_response = await narrator.progress_story(direct_narrative)
+ if not narrator_response:
+ return None
+ narrator_message = NarratorMessage(narrator_response, source="progress_story")
+ self.scene.push_history(narrator_message)
+ emit("narrator", narrator_message)
+ return True
+ @set_processing
async def direct_narrative(self, analysis:str, narration_type:str="progress", goal:str=None):
if goal is None:
@@ -120,6 +118,7 @@ class DirectorAgent(ConversationAgent):
return response
+ @set_processing
async def direct_character_with_self_reflection(self, character: Character, analysis:str, goal_override:str=None):
max_retries = 3
@@ -162,6 +161,7 @@ class DirectorAgent(ConversationAgent):
return response
+ @set_processing
async def transform_character_direction_to_inner_monologue(self, character:Character, direction:str):
inner_monologue = await Prompt.request(
@@ -179,6 +179,7 @@ class DirectorAgent(ConversationAgent):
return inner_monologue
+ @set_processing
async def direct_character(
self,
character: Character,
@@ -229,6 +230,7 @@ class DirectorAgent(ConversationAgent):
+ @set_processing
async def direct_character_self_reflect(self, direction:str, character: Character, goal:str, direction_prompt:Prompt) -> (bool, str):
change_matches = ["change", "retry", "alter", "reconsider"]
@@ -253,6 +255,7 @@ class DirectorAgent(ConversationAgent):
return keep, response
+ @set_processing
async def direct_character_analyze(self, direction:str, character: Character, goal:str, direction_prompt:Prompt):
prompt = Prompt.get("director.direct-character-analyze", vars={
@@ -317,6 +320,7 @@ class DirectorAgent(ConversationAgent):
else:
return ""
+ @set_processing
async def goal_analyze(self, goal:str):
prompt = Prompt.get("director.goal-analyze", vars={
diff --git a/src/talemate/agents/narrator.py b/src/talemate/agents/narrator.py
index 603edba6..7549f582 100644
--- a/src/talemate/agents/narrator.py
+++ b/src/talemate/agents/narrator.py
@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Callable, List, Optional, Union
import talemate.util as util
from talemate.emit import wait_for_input
from talemate.prompts import Prompt
+from talemate.agents.base import set_processing
from .conversation import ConversationAgent
from .registry import register
@@ -23,10 +24,6 @@ class NarratorAgent(ConversationAgent):
if "#" in result:
result = result.split("#")[0]
-
- # Removes partial sentence at the end
- # result = re.sub(r"[^\.\?\!]+(\n|$)", "", result)
-
cleaned = []
for line in result.split("\n"):
if ":" in line.strip():
@@ -35,14 +32,12 @@ class NarratorAgent(ConversationAgent):
return "\n".join(cleaned)
+ @set_processing
async def narrate_scene(self):
"""
Narrate the scene
"""
- await self.emit_status(processing=True)
-
-
response = await Prompt.request(
"narrator.narrate-scene",
self.client,
@@ -55,17 +50,14 @@ class NarratorAgent(ConversationAgent):
response = f"*{response.strip('*')}*"
- await self.emit_status(processing=False)
-
return response
+ @set_processing
async def progress_story(self, narrative_direction:str=None):
"""
Narrate the scene
"""
- await self.emit_status(processing=True)
-
scene = self.scene
director = scene.get_helper("director").agent
pc = scene.get_player_character()
@@ -113,17 +105,13 @@ class NarratorAgent(ConversationAgent):
response = response.replace("*", "")
response = f"*{response}*"
- await self.emit_status(processing=False)
-
return response
+ @set_processing
async def narrate_query(self, query:str, at_the_end:bool=False, as_narrative:bool=True):
"""
Narrate a specific query
"""
-
- await self.emit_status(processing=True)
-
response = await Prompt.request(
"narrator.narrate-query",
self.client,
@@ -141,15 +129,14 @@ class NarratorAgent(ConversationAgent):
if as_narrative:
response = f"*{response}*"
- await self.emit_status(processing=False)
return response
+ @set_processing
async def narrate_character(self, character):
"""
Narrate a specific character
"""
- await self.emit_status(processing=True)
budget = self.client.max_token_length - 300
memory_budget = min(int(budget * 0.05), 200)
@@ -176,11 +163,9 @@ class NarratorAgent(ConversationAgent):
response = self.clean_result(response.strip())
response = f"*{response}*"
- await self.emit_status(processing=False)
return response
-
-
+ @set_processing
async def augment_context(self):
"""
diff --git a/src/talemate/agents/summarize.py b/src/talemate/agents/summarize.py
index 30cc0baa..11cd9648 100644
--- a/src/talemate/agents/summarize.py
+++ b/src/talemate/agents/summarize.py
@@ -8,7 +8,7 @@ import talemate.util as util
from talemate.prompts import Prompt
from talemate.scene_message import DirectorMessage
-from .base import Agent
+from .base import Agent, set_processing
from .registry import register
import structlog
@@ -40,6 +40,7 @@ class SummarizeAgent(Agent):
super().connect(scene)
scene.signals["history_add"].connect(self.on_history_add)
+ @set_processing
async def build_archive(self, scene):
end = None
@@ -67,7 +68,6 @@ class SummarizeAgent(Agent):
if end is None:
# nothing to archive yet
return
- await self.emit_status(processing=True)
extra_context = None
if recent_entry:
@@ -91,13 +91,12 @@ class SummarizeAgent(Agent):
)
scene.push_archive(data_objects.ArchiveEntry(summarized, start, end))
- await self.emit_status(processing=False)
return True
+ @set_processing
async def analyze_dialoge(self, dialogue):
instruction = "Examine the dialogue from the beginning and find the first line that marks a scene change. Repeat the line back to me exactly as it is written"
- await self.emit_status(processing=True)
prepare_response = "The first line that marks a scene change is: "
@@ -110,10 +109,9 @@ class SummarizeAgent(Agent):
response = self.clean_result(response)
- await self.emit_status(processing=False)
-
return response
+ @set_processing
async def summarize(
self,
text: str,
@@ -125,8 +123,6 @@ class SummarizeAgent(Agent):
Summarize the given text
"""
- await self.emit_status(processing=True)
-
response = await Prompt.request("summarizer.summarize-dialogue", self.client, "summarize", vars={
"dialogue": text,
"scene": self.scene,
@@ -135,14 +131,12 @@ class SummarizeAgent(Agent):
self.scene.log.info("summarize", dialogue=text, response=response)
- await self.emit_status(processing=False)
-
return self.clean_result(response)
+ @set_processing
async def simple_summary(
self, text: str, prompt_kind: str = "summarize", instructions: str = "Summarize"
):
- await self.emit_status(processing=True)
prompt = [
text,
"",
@@ -153,62 +147,52 @@ class SummarizeAgent(Agent):
response = await self.client.send_prompt("\n".join(map(str, prompt)), kind=prompt_kind)
if ":" in response:
response = response.split(":")[1].strip()
- await self.emit_status(processing=False)
return response
+ @set_processing
async def request_world_state(self):
- await self.emit_status(processing=True)
- try:
-
- t1 = time.time()
+ t1 = time.time()
- _, world_state = await Prompt.request(
- "summarizer.request-world-state",
- self.client,
- "analyze",
- vars = {
- "scene": self.scene,
- "max_tokens": self.client.max_token_length,
- "object_type": "character",
- "object_type_plural": "characters",
- }
- )
-
- self.scene.log.debug("request_world_state", response=world_state, time=time.time() - t1)
-
- return world_state
- finally:
- await self.emit_status(processing=False)
+ _, world_state = await Prompt.request(
+ "summarizer.request-world-state",
+ self.client,
+ "analyze",
+ vars = {
+ "scene": self.scene,
+ "max_tokens": self.client.max_token_length,
+ "object_type": "character",
+ "object_type_plural": "characters",
+ }
+ )
+
+ self.scene.log.debug("request_world_state", response=world_state, time=time.time() - t1)
+
+ return world_state
+ @set_processing
async def request_world_state_inline(self):
"""
EXPERIMENTAL, Overall the one shot request seems about as coherent as the inline request, but the inline request is is about twice as slow and would need to run on every dialogue line.
"""
- await self.emit_status(processing=True)
- try:
-
- t1 = time.time()
+ t1 = time.time()
- # first, we need to get the marked items (objects etc.)
+ # first, we need to get the marked items (objects etc.)
- marked_items_response = await Prompt.request(
- "summarizer.request-world-state-inline-items",
- self.client,
- "analyze_freeform",
- vars = {
- "scene": self.scene,
- "max_tokens": self.client.max_token_length,
- }
- )
-
- self.scene.log.debug("request_world_state_inline", marked_items=marked_items_response, time=time.time() - t1)
-
- return marked_items_response
- finally:
- await self.emit_status(processing=False)
-
\ No newline at end of file
+ marked_items_response = await Prompt.request(
+ "summarizer.request-world-state-inline-items",
+ self.client,
+ "analyze_freeform",
+ vars = {
+ "scene": self.scene,
+ "max_tokens": self.client.max_token_length,
+ }
+ )
+
+ self.scene.log.debug("request_world_state_inline", marked_items=marked_items_response, time=time.time() - t1)
+
+ return marked_items_response
\ No newline at end of file
diff --git a/src/talemate/client/context.py b/src/talemate/client/context.py
index a89187f6..05506232 100644
--- a/src/talemate/client/context.py
+++ b/src/talemate/client/context.py
@@ -11,11 +11,17 @@ __all__ = [
'ContextModel',
]
+
+class ConversationContext(BaseModel):
+ talking_character: str = None
+ other_characters: list[str] = Field(default_factory=list)
+
class ContextModel(BaseModel):
"""
Pydantic model for the context data.
"""
nuke_repetition: float = Field(0.0, ge=0.0, le=3.0)
+ conversation: ConversationContext = Field(default_factory=ConversationContext)
# Define the context variable as an empty dictionary
context_data = ContextVar('context_data', default=ContextModel().dict())
diff --git a/src/talemate/client/openai.py b/src/talemate/client/openai.py
index ed6a7fcc..633a7749 100644
--- a/src/talemate/client/openai.py
+++ b/src/talemate/client/openai.py
@@ -9,7 +9,6 @@ from talemate.client.registry import register
from talemate.emit import emit
from talemate.config import load_config
import talemate.client.system_prompts as system_prompts
-
import structlog
__all__ = [
@@ -142,5 +141,14 @@ class OpenAIClient:
log.debug("openai response", response=response)
+ emit("prompt_sent", data={
+ "kind": kind,
+ "prompt": prompt,
+ "response": response,
+ # TODO use tiktoken
+ "prompt_tokens": "?",
+ "response_tokens": "?",
+ })
+
self.emit_status(processing=False)
return response
diff --git a/src/talemate/client/textgenwebui.py b/src/talemate/client/textgenwebui.py
index 02e44440..62ae7d7b 100644
--- a/src/talemate/client/textgenwebui.py
+++ b/src/talemate/client/textgenwebui.py
@@ -417,11 +417,21 @@ class TextGeneratorWebuiClient(RESTTaleMateClient):
prompt,
)
+ stopping_strings = ["<|end_of_turn|>"]
+
+ conversation_context = client_context_attribute("conversation")
+
+ stopping_strings += [
+ f"{character}:" for character in conversation_context["other_characters"]
+ ]
+
+ log.debug("prompt_config_conversation", stopping_strings=stopping_strings, conversation_context=conversation_context)
+
config = {
"prompt": prompt,
"max_new_tokens": 75,
"chat_prompt_size": self.max_token_length,
- "stopping_strings": ["<|end_of_turn|>", "\n\n"],
+ "stopping_strings": stopping_strings,
}
config.update(PRESET_TALEMATE_CONVERSATION)
@@ -616,7 +626,15 @@ class TextGeneratorWebuiClient(RESTTaleMateClient):
response = response.split("#")[0]
self.emit_status(processing=False)
- await asyncio.sleep(0.01)
+
+ emit("prompt_sent", data={
+ "kind": kind,
+ "prompt": message["prompt"],
+ "response": response,
+ "prompt_tokens": token_length,
+ "response_tokens": int(len(response) / 3.6)
+ })
+
return response
diff --git a/src/talemate/emit/signals.py b/src/talemate/emit/signals.py
index 17a7ba1b..b886d4a2 100644
--- a/src/talemate/emit/signals.py
+++ b/src/talemate/emit/signals.py
@@ -14,6 +14,7 @@ ReceiveInput = signal("receive_input")
ClientStatus = signal("client_status")
AgentStatus = signal("agent_status")
ClientBootstraps = signal("client_bootstraps")
+PromptSent = signal("prompt_sent")
RemoveMessage = signal("remove_message")
@@ -42,4 +43,5 @@ handlers = {
"world_state": WorldState,
"archived_history": ArchivedHistory,
"message_edited": MessageEdited,
+ "prompt_sent": PromptSent,
}
diff --git a/src/talemate/prompts/templates/conversation/dialogue.jinja2 b/src/talemate/prompts/templates/conversation/dialogue.jinja2
index 0269a568..515f99a0 100644
--- a/src/talemate/prompts/templates/conversation/dialogue.jinja2
+++ b/src/talemate/prompts/templates/conversation/dialogue.jinja2
@@ -5,7 +5,9 @@
<|CLOSE_SECTION|>
<|SECTION:CHARACTERS|>
{% for character in characters -%}
-{{ character.name }}: {{ character.description }}
+{{ character.name }}:
+{{ character.filtered_sheet(['name', 'description', 'age', 'gender']) }}
+{{ query_memory(character.name+' personality', as_question_answer= False) }}
{% endfor %}
<|CLOSE_SECTION|>
@@ -28,9 +30,7 @@ Based on {{ talking_character.name}}'s example dialogue style, create a continua
You may chose to have {{ talking_character.name}} respond to {{main_character.name}}'s last message, or you may chose to have {{ talking_character.name}} perform a new action that is in line with {{ talking_character.name}}'s character.
-{% if scene.history and scene.history[-1].type == "director" -%}
-Follow the instructions to you for your next message as {{ talking_character.name}}. NEVER directly respond to the instructions, but use the direction we have given you as you perform {{ talking_character.name }}'s response to {{main_character.name}}. You can separate thoughts and actual dialogue by containing thoughts inside curly brackets. Example: "{stuff you want to keep private} stuff you want to say publicly."
-{% endif -%}
+Use an informal and colloquial register with a conversational toneā¦Overall, their dialog is Informal, conversational, natural, and spontaneous, with a sense of immediacy.
<|CLOSE_SECTION|>
<|SECTION:SCENE|>
diff --git a/src/talemate/prompts/templates/creator/character-attributes-human.jinja2 b/src/talemate/prompts/templates/creator/character-attributes-human.jinja2
index d2090b51..442b175b 100644
--- a/src/talemate/prompts/templates/creator/character-attributes-human.jinja2
+++ b/src/talemate/prompts/templates/creator/character-attributes-human.jinja2
@@ -48,12 +48,12 @@ Examples: John, Mary, Jane, Bob, Alice, etc.
Respond with a number only
{% endif -%}
{% if character_sheet.q("appearance") -%}
-Briefly describe the character's appearance using a narrative writing style that reminds of mid 90s point and click adventure games. (2 - 3 sentences). {{ spice("Make it {spice}.", spices) }}
+Briefly describe the character's appearance using a narrative writing style that reminds of mid 90s point and click adventure games. (1 - 2 sentences). {{ spice("Make it {spice}.", spices) }}
{% endif -%}
{% block generate_appearance %}
{% endblock %}
{% if character_sheet.q("personality") -%}
-Briefly describe the character's personality using a narrative writing style that reminds of mid 90s point and click adventure games. (2 - 3 sentences). {{ spice("Make it {spice}.", spices) }}
+Briefly describe the character's personality using a narrative writing style that reminds of mid 90s point and click adventure games. (1 - 2 sentences). {{ spice("Make it {spice}.", spices) }}
{% endif -%}
{% if character_sheet.q("family and fiends") %}
List close family and friends of {{ character_sheet("name") }}. Respond with a comma separated list of names. (2 - 3 names, include age)
@@ -69,7 +69,7 @@ List some things that {{ character_sheet("name") }} dislikes. Respond with a com
Examples: cats, dogs, pizza, etc.
{% endif -%}
{% if character_sheet.q("clothes and accessories") -%}
-Briefly describe the character's clothes and accessories using a narrative writing style that reminds of mid 90s point and click adventure games. (2 - 3 sentences). {{ spice("Make it {spice}.", spices) }}
+Briefly describe the character's clothes and accessories using a narrative writing style that reminds of mid 90s point and click adventure games. (1 - 2 sentences). {{ spice("Make it {spice}.", spices) }}
{% endif %}
{% block generate_misc %}{% endblock -%}
{% for custom_attribute, instructions in custom_attributes.items() -%}
diff --git a/src/talemate/prompts/templates/creator/character-description.jinja2 b/src/talemate/prompts/templates/creator/character-description.jinja2
index fb1b43b8..0137949e 100644
--- a/src/talemate/prompts/templates/creator/character-description.jinja2
+++ b/src/talemate/prompts/templates/creator/character-description.jinja2
@@ -4,6 +4,6 @@
<|SECTION:TASK|>
Summarize {{ character.name }} based on the character sheet above.
-Use a narrative writing style that reminds of mid 90s point and click adventure games about a {{ content_context }}
+Use a narrative writing style that reminds of mid 90s point and click adventure games about {{ content_context }}
<|CLOSE_SECTION|>
{{ set_prepared_response(character.name+ " is ") }}
\ No newline at end of file
diff --git a/src/talemate/server/character_creator.py b/src/talemate/server/character_creator.py
index 22247d12..bfea5517 100644
--- a/src/talemate/server/character_creator.py
+++ b/src/talemate/server/character_creator.py
@@ -148,13 +148,13 @@ class CharacterCreatorServerPlugin:
async def handle_submit_step3(self, data:dict):
creator = self.scene.get_helper("creator").agent
- character, _ = self.apply_step_data(data)
+ character, step_data = self.apply_step_data(data)
self.emit_step_start(3)
description = await creator.create_character_description(
character,
- content_context=self.character_creation_data.scenario_context,
+ content_context=step_data.scenario_context,
)
character.description = description
diff --git a/src/talemate/server/websocket_server.py b/src/talemate/server/websocket_server.py
index ea6d1703..6e48c1fa 100644
--- a/src/talemate/server/websocket_server.py
+++ b/src/talemate/server/websocket_server.py
@@ -292,6 +292,14 @@ class WebsocketHandler(Receiver):
}
)
+ def handle_prompt_sent(self, emission: Emission):
+ self.queue_put(
+ {
+ "type": "prompt_sent",
+ "data": emission.data,
+ }
+ )
+
def handle_clear_screen(self, emission: Emission):
self.queue_put(
{
diff --git a/src/talemate/tale_mate.py b/src/talemate/tale_mate.py
index afddfe05..4a3c088a 100644
--- a/src/talemate/tale_mate.py
+++ b/src/talemate/tale_mate.py
@@ -23,6 +23,7 @@ from talemate.exceptions import ExitScene, RestartSceneLoop, ResetScene, Talemat
from talemate.world_state import WorldState
from talemate.config import SceneConfig
from talemate.scene_assets import SceneAssets
+from talemate.client.context import ClientContext, ConversationContext
import talemate.automated_action as automated_action
@@ -139,6 +140,23 @@ class Character:
return ""
return random.choice(self.example_dialogue)
+
+ def filtered_sheet(self, attributes: list[str]):
+
+ """
+ Same as sheet but only returns the attributes in the given list
+
+ Attributes that dont exist will be ignored
+ """
+
+ sheet_list = []
+
+ for key, value in self.base_attributes.items():
+ if key.lower() not in attributes:
+ continue
+ sheet_list.append(f"{key}: {value}")
+
+ return "\n".join(sheet_list)
def save(self, file_path: str):
"""
@@ -413,8 +431,14 @@ class Actor:
self.agent.character = self.character
- messages = await self.agent.converse(self, editor=editor)
- await asyncio.sleep(0)
+ conversation_context = ConversationContext(
+ talking_character=self.character.name,
+ other_characters=[actor.character.name for actor in self.scene.actors if actor != self],
+ )
+
+ with ClientContext(conversation=conversation_context):
+ messages = await self.agent.converse(self, editor=editor)
+
return messages
diff --git a/talemate_frontend/src/components/DebugToolPromptLog.vue b/talemate_frontend/src/components/DebugToolPromptLog.vue
new file mode 100644
index 00000000..7841debd
--- /dev/null
+++ b/talemate_frontend/src/components/DebugToolPromptLog.vue
@@ -0,0 +1,86 @@
+
+ mdi-post-outline Prompts
+ {{ max_prompts }}
+
+
+
+
+
+
+
+
+ {{ prompt.kind }}
+
+
+ mdi-pound{{ prompt.num }}
+ {{ prompt.prompt_tokens }}mdi-arrow-down-bold
+ {{ prompt.response_tokens }}mdi-arrow-up-bold
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/talemate_frontend/src/components/DebugToolPromptView.vue b/talemate_frontend/src/components/DebugToolPromptView.vue
new file mode 100644
index 00000000..b5306e8d
--- /dev/null
+++ b/talemate_frontend/src/components/DebugToolPromptView.vue
@@ -0,0 +1,68 @@
+
+
+
+
+ #{{ prompt.num }} - {{ prompt.kind }}
+
+
+
+ Prompt
+
+
+ Response
+
+
+
+
+
+
+
+ {{ prompt.prompt }}
+
+
+
+
+
+
+ {{ prompt.response }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/talemate_frontend/src/components/DebugTools.vue b/talemate_frontend/src/components/DebugTools.vue
new file mode 100644
index 00000000..41e0bdc8
--- /dev/null
+++ b/talemate_frontend/src/components/DebugTools.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/talemate_frontend/src/components/SceneMessages.vue b/talemate_frontend/src/components/SceneMessages.vue
index e333489f..bd366703 100644
--- a/talemate_frontend/src/components/SceneMessages.vue
+++ b/talemate_frontend/src/components/SceneMessages.vue
@@ -160,7 +160,7 @@ export default {
return
}
-
+
if (data.message) {
if (data.type === 'character') {
const [character, text] = data.message.split(':');
diff --git a/talemate_frontend/src/components/TalemateApp.vue b/talemate_frontend/src/components/TalemateApp.vue
index 8f405523..e3cf8ae5 100644
--- a/talemate_frontend/src/components/TalemateApp.vue
+++ b/talemate_frontend/src/components/TalemateApp.vue
@@ -53,6 +53,14 @@
+
+
+
+ mdi-bug Debug Tools
+
+
+
+
@@ -94,6 +102,7 @@
Talemate
+ mdi-bug
mdi-cog
mdi-application-cog
@@ -145,6 +154,7 @@ import CharacterSheet from './CharacterSheet.vue';
import SceneHistory from './SceneHistory.vue';
import CreativeEditor from './CreativeEditor.vue';
import AppConfig from './AppConfig.vue';
+import DebugTools from './DebugTools.vue';
export default {
components: {
@@ -160,6 +170,7 @@ export default {
SceneHistory,
CreativeEditor,
AppConfig,
+ DebugTools,
},
name: 'TalemateApp',
data() {
@@ -169,6 +180,7 @@ export default {
sceneActive: false,
drawer: false,
sceneDrawer: true,
+ debugDrawer: false,
websocket: null,
inputDisabled: false,
waitingForInput: false,
@@ -369,6 +381,8 @@ export default {
this.sceneDrawer = !this.sceneDrawer;
else if (navigation == "settings")
this.drawer = !this.drawer;
+ else if (navigation == "debug")
+ this.debugDrawer = !this.debugDrawer;
},
getClients() {
if (!this.$refs.aiClient) {
diff --git a/templates/llm-prompt/Xwin-LM.jinja2 b/templates/llm-prompt/Xwin-LM.jinja2
new file mode 100644
index 00000000..b9f3762e
--- /dev/null
+++ b/templates/llm-prompt/Xwin-LM.jinja2
@@ -0,0 +1 @@
+{{ system_message }} USER: {{ set_response(prompt, " ASSISTANT:") }}
\ No newline at end of file