Chat renaming, JSON lead
Chat renames automatically JSON: lead added to AI messages to force json output
frdel committed
Apr 5, 2025 at 21:06 UTC
bf43c60d10092ef17504f316938da463fe7fd245
9 files changed
+105
-13
agent.py
+1
@@ -371,6 +371,7 @@ class Agent:
371
[
372
SystemMessage(content=system_text),
373
*history_langchain,
374
+ AIMessage(content="JSON:"), # force the LLM to start with json
375
]
376
)
377
models.py
+1
-1
@@ -332,7 +332,7 @@ def get_openrouter_chat(
332
dotenv.get_dotenv_value("OPEN_ROUTER_BASE_URL")
333
or "https://openrouter.ai/api/v1"
334
)
335
- return ChatOpenAI(api_key=api_key, model=model_name, base_url=base_url, **kwargs) # type: ignore
335
+ return ChatOpenAI(api_key=api_key, model=model_name, base_url=base_url, stream_usage=True, **kwargs) # type: ignore
336
337
338
def get_openrouter_embedding(
prompts/default/fw.rename_chat.msg.md
new
+8
@@ -0,0 +1,8 @@
1
+# Instruction
2
+- provide a chat name for the following
3
+
4
+# Current chat name
5
+{{current_name}}
6
+
7
+# Chat history
8
+{{history}}
prompts/default/fw.rename_chat.sys.md
new
+19
@@ -0,0 +1,19 @@
1
+# AI role
2
+- You are a chat naming assistant
3
+- Your role is to suggest a short chat name for the current conversation
4
+
5
+# Input
6
+- You are given the current chat name and current chat history
7
+
8
+# Output
9
+- Respond with a short chat name (1-3 words) based on the chat history
10
+- Consider current chat name and only change it when the conversation topic has changed
11
+- Focus mainly on the end of the conversation history, there you can detect if the topic has changed
12
+- Only respond with the chat name without any formatting, intro or additional text
13
+- Maintain proper capitalization
14
+
15
+# Example responses
16
+Database setup
17
+Requirements installation
18
+Merging documents
19
+Image analysis
\ No newline at end of file
python/api/poll.py
+1
@@ -19,6 +19,7 @@ class Poll(ApiHandler):
19
ctxs.append(
20
{
21
"id": ctx.id,
22
+ "name": ctx.name,
23
"no": ctx.no,
24
"log_guid": ctx.log.guid,
25
"log_version": len(ctx.log.updates),
python/extensions/monologue_end/_60_rename_chat.py
new
+37
@@ -0,0 +1,37 @@
1
+from python.helpers import persist_chat, tokens
2
+from python.helpers.extension import Extension
3
+from agent import LoopData
4
+import asyncio
5
+
6
+
7
+class RenameChat(Extension):
8
+
9
+ async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
10
+ asyncio.create_task(self.change_name())
11
+
12
+ async def change_name(self):
13
+ try:
14
+ # prepare history
15
+ history_text = self.agent.history.output_text()
16
+ ctx_length = int(self.agent.config.utility_model.ctx_length * 0.7)
17
+ history_text = tokens.trim_to_tokens(history_text, ctx_length, "start")
18
+ # prepare system and user prompt
19
+ system = self.agent.read_prompt("fw.rename_chat.sys.md")
20
+ current_name = self.agent.context.name
21
+ message = self.agent.read_prompt(
22
+ "fw.rename_chat.msg.md", current_name=current_name, history=history_text
23
+ )
24
+ # call utility model
25
+ new_name = await self.agent.call_utility_model(
26
+ system=system, message=message, background=True
27
+ )
28
+ # update name
29
+ if new_name:
30
+ # trim name to max length if needed
31
+ if len(new_name) > 40:
32
+ new_name = new_name[:40] + "..."
33
+ # apply to context and save
34
+ self.agent.context.name = new_name
35
+ persist_chat.save_tmp_chat(self.agent.context)
36
+ except Exception as e:
37
+ pass # non-critical
python/helpers/persist_chat.py
+1
@@ -89,6 +89,7 @@ def _serialize_context(context: AgentContext):
89
90
return {
91
"id": context.id,
92
+ "name": context.name,
93
"agents": agents,
94
"streaming_agent": (
95
context.streaming_agent.number if context.streaming_agent else 0
python/helpers/tokens.py
+36
-11
@@ -1,19 +1,44 @@
1
+from typing import Literal
2
import tiktoken
3
4
APPROX_BUFFER = 1.1
5
+TRIM_BUFFER = 0.8
6
+
7
8
def count_tokens(text: str, encoding_name="cl100k_base") -> int:
6
- if not text:
7
- return 0
8
-
9
- # Get the encoding
10
- encoding = tiktoken.get_encoding(encoding_name)
9
+ if not text:
10
+ return 0
11
+
12
+ # Get the encoding
13
+ encoding = tiktoken.get_encoding(encoding_name)
14
+
15
+ # Encode the text and count the tokens
16
+ tokens = encoding.encode(text)
17
+ token_count = len(tokens)
18
+
19
+ return token_count
20
+
21
+
22
+def approximate_tokens(
23
+ text: str,
24
+) -> int:
25
+ return int(count_tokens(text) * APPROX_BUFFER)
26
+
27
+
28
+def trim_to_tokens(
29
+ text: str,
30
+ max_tokens: int,
31
+ direction: Literal["start", "end"],
32
+ ellipsis: str = "...",
33
+) -> str:
34
+ chars = len(text)
35
+ tokens = count_tokens(text)
36
12
- # Encode the text and count the tokens
13
- tokens = encoding.encode(text)
14
- token_count = len(tokens)
37
+ if tokens <= max_tokens:
38
+ return text
39
16
- return token_count
40
+ approx_chars = int(chars * (max_tokens / tokens) * TRIM_BUFFER)
41
18
-def approximate_tokens(text: str, ) -> int:
19
- return int(count_tokens(text) * APPROX_BUFFER)
\ No newline at end of file
42
+ if direction == "start":
43
+ return text[:approx_chars] + ellipsis
44
+ return ellipsis + text[chars - approx_chars : chars]
webui/index.html
+1
-1
@@ -97,7 +97,7 @@
97
<li>
98
<span :class="{'chat-list-button': true, 'font-bold': context.id === selected}"
99
@click="selected = context.id; selectChat(context.id)">
100
- Chat #<span x-text="context.no"></span>
100
+ <span x-text="context.name ? context.name : 'Chat #' + context.no"></span>
101
</span>
102
<button class="edit-button" @click="killChat(context.id)">X</button>
103
</li>