diff src/lib/ai/claude/Ai_chat.luan @ 19:0351b3d474f8

minor
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 30 Jul 2025 15:28:09 -0600
parents src/lib/ai/claude/Chat.luan@f5425a3c1898
children 27989d63fc71
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/ai/claude/Ai_chat.luan	Wed Jul 30 15:28:09 2025 -0600
@@ -0,0 +1,129 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local ipairs = Luan.ipairs or error()
+local type = Luan.type or error()
+local String = require "luan:String.luan"
+local starts_with = String.starts_with or error()
+local Html = require "luan:Html.luan"
+local html_encode = Html.encode or error()
+local Parsers = require "luan:Parsers.luan"
+local json_parse = Parsers.json_parse or error()
+local json_string = Parsers.json_string or error()
+local Claude = require "site:/lib/ai/claude/Claude.luan"
+local claude_chat = Claude.chat or error()
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "claude/Ai_chat"
+
+
+local Ai_chat = {}
+
+function Ai_chat.output_system_prompt(thread)
+	if thread == nil then
+		return
+	end
+	thread = json_parse(thread)
+	local system_prompt = thread.system or error
+	system_prompt = html_encode(system_prompt)
+	%><%=system_prompt%><%
+end
+
+function Ai_chat.output_messages_html(lang,thread,old_thread)
+	if thread == nil then
+		return
+	end
+	thread = json_parse(thread)
+	local messages = thread.messages or error
+	local n = 0
+	if old_thread ~= nil then
+		old_thread = json_parse(old_thread)
+		local old_messages = old_thread.messages or error
+		n = #old_messages
+	end
+	for i, message in ipairs(messages) do
+		if i <= n then
+			continue
+		end
+		local role = message.role or error()
+		local who
+		if role=="assistant" then
+			who = "Claude"
+		elseif role=="user" then
+			who = "You"
+		else
+			error(role)
+		end
+		local function output(text)
+			text = html_encode(text)
+%>
+			<h3><%=who%></h3>
+			<div markdown role="<%=role%>" lang="<%=lang%>"><%=text%></div>
+<%
+		end
+		local content = message.content or error()
+		if type(content) == "string" then
+			output(content)
+		else
+			for _, part in ipairs(content) do
+				if part.type=="text" then
+					local text = part.text or error()
+					output(text)
+				end
+			end
+		end
+	end_for
+end
+
+
+local system_prompt = [[
+CRITICAL REQUIREMENT: When writing Japanese, use ruby markdown syntax {japanese|romaji} for pronunciation guidance.
+
+Apply ruby tags to meaningful pronunciation units:
+- Individual kanji or kanji compounds: {私|watashi}, {学生|gakusei}
+- Hiragana/katakana words and particles: {は|wa}, {です|desu}, {ありがとう|arigatō}
+- Grammatical elements: {ました|mashita}, {ません|masen}
+
+The romaji must reflect ACTUAL PRONUNCIATION, not character-by-character readings.
+Use macrons for long vowels: ā, ī, ū, ē, ō
+
+APPLIES TO ALL JAPANESE TEXT: Example sentences, grammar explanations, vocabulary lists, casual mentions - ANY Japanese characters in your response need ruby tags.
+
+VERIFICATION STEP: Before sending, scan your ENTIRE response for any Japanese characters (hiragana, katakana, kanji) and ensure they all have ruby tags.
+]]
+
+
+function Ai_chat.ask(thread,input)
+	thread = thread and json_parse(thread) or {
+		system = system_prompt
+		messages = {nil}
+	}
+	local messages = thread.messages or error
+	messages[#messages+1] = {
+		role = "user"
+		content = input
+	}
+--[=[
+	messages[#messages+1] = {
+		role = "assistant"
+		content = [[
+hello
+]]
+	}
+	if true then
+		return json_string(thread)
+	end
+--]=]
+	local resultJson = claude_chat(thread)
+	local result = json_parse(resultJson)
+	-- logger.info(json_string(result))
+	result.type == "message" or error()
+	result.role == "assistant" or error()
+	result.stop_reason == "end_turn" or result.stop_reason == "tool_use" or error()
+	local content = result.content or error()
+	messages[#messages+1] = {
+		role = "assistant"
+		content = content
+	}
+	return json_string(thread)
+end
+
+return Ai_chat