changeset 76:3c60bc17403e default tip

remove ai
author Franklin Schmidt <fschmidt@gmail.com>
date Sun, 31 Aug 2025 17:40:20 -0600
parents b96cf27e719d
children
files src/lib/Chat.luan src/lib/ai/Ai.luan src/lib/ai/claude/Ai_chat.luan src/lib/ai/claude/Claude.luan src/lib/ai/claude/Translator.luan src/lib/claude/Ai_chat.luan src/lib/claude/Claude.luan src/lib/claude/Translator.luan src/new_chat.red.luan src/translate.js.luan
diffstat 10 files changed, 342 insertions(+), 363 deletions(-) [+]
line wrap: on
line diff
diff -r b96cf27e719d -r 3c60bc17403e src/lib/Chat.luan
--- a/src/lib/Chat.luan	Thu Aug 28 14:36:03 2025 -0600
+++ b/src/lib/Chat.luan	Sun Aug 31 17:40:20 2025 -0600
@@ -9,7 +9,7 @@
 local html_encode = Html.encode or error()
 local Db = require "site:/lib/Db.luan"
 local run_in_transaction = Db.run_in_transaction or error()
-local Ai_chat = require "site:/lib/ai/claude/Ai_chat.luan"
+local Ai_chat = require "site:/lib/claude/Ai_chat.luan"
 local Course = require "site:/lib/Course.luan"
 local get_course_by_id = Course.get_by_id or error()
 local Shared = require "site:/lib/Shared.luan"
diff -r b96cf27e719d -r 3c60bc17403e src/lib/ai/Ai.luan
--- a/src/lib/ai/Ai.luan	Thu Aug 28 14:36:03 2025 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-local Luan = require "luan:Luan.luan"
-local error = Luan.error
-local ipairs = Luan.ipairs or error()
-
-
-error "not used for now"
-
-local Ai = {}
-
-local ais = {"claude"}
-local files = {"Ai_chat.luan"}
-for _, ai in ipairs(ais) do
-	local mods = {}
-	local dir = "site:/lib/ai/"..ai.."/"
-	for _, file in ipairs(files) do
-		mods[file] = require(dir..file)
-	end
-	Ai[ai] = mods
-end
-
-return Ai
diff -r b96cf27e719d -r 3c60bc17403e src/lib/ai/claude/Ai_chat.luan
--- a/src/lib/ai/claude/Ai_chat.luan	Thu Aug 28 14:36:03 2025 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,255 +0,0 @@
-local Luan = require "luan:Luan.luan"
-local error = Luan.error
-local ipairs = Luan.ipairs or error()
-local pairs = Luan.pairs 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 Thread = require "luan:Thread.luan"
-local Claude = require "site:/lib/ai/claude/Claude.luan"
-local claude_chat = Claude.chat or error()
-local Utils = require "site:/lib/Utils.luan"
-local deep_copy = Utils.deep_copy 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)
-	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(assistant_controls,thread,old_thread)
-	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)
-%>
-			<div role="<%=role%>" msg="<%=i%>">
-				<h3><%=who%></h3>
-				<div message markdown><%=text%></div>
-<%			if role=="assistant" then %>
-<%=				assistant_controls %>
-<%			end %>
-			</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 function get_chat(chat_id)
-	local Chat = require "site:/lib/Chat.luan"
-	local User = require "site:/lib/User.luan"
-	local chat = Chat.get_by_id(chat_id) or error()
-	local user = User.current()
-	local is_owner = user ~= nil and user.id == chat.user_id
-	is_owner or not chat.is_private or error "private"
-	return chat
-end
-
-local functions = {
-	get_chat = {
-		tool = {
-			description = "Get the contents of a chat/thread with Claude on this website.  The contents will be JSON in the format of the Claude API."
-			input_schema = {
-				type = "object"
-				properties = {
-					chat_id = {
-						description = "The ID of the chat"
-						type = "integer"
-					}
-				}
-			}
-		}
-		fn = function(input)
-			local chat_id = input.chat_id or error()
-			local chat = get_chat(chat_id)
-			return chat.ai_thread or error()
-		end
-	}
-	get_tts_instructions = {
-		tool = {
-			description = "Get the text-to-speech instructions of a chat/thread on this website.  These instructions are passed to OpenAI.  If there are no instructions, the empty string is returned."
-			input_schema = {
-				type = "object"
-				properties = {
-					chat_id = {
-						description = "The ID of the chat"
-						type = "integer"
-					}
-				}
-			}
-		}
-		fn = function(input)
-			local chat_id = input.chat_id or error()
-			local chat = get_chat(chat_id)
-			return chat.tts_instructions or error()
-		end
-	}
-	get_stt_prompt = {
-		tool = {
-			description = "Get the speech-to-text prompt of a chat/thread on this website.  This prompt is passed to OpenAI.  If there is no prompt, the empty string is returned."
-			input_schema = {
-				type = "object"
-				properties = {
-					chat_id = {
-						description = "The ID of the chat"
-						type = "integer"
-					}
-				}
-			}
-		}
-		fn = function(input)
-			local chat_id = input.chat_id or error()
-			local chat = get_chat(chat_id)
-			return chat.stt_prompt or error()
-		end
-	}
-}
-local tools = {nil}
-for name, f in pairs(functions) do
-	f.name = name
-	f.tool.name = name
-	tools[#tools+1] = f.tool
-end
-
-function Ai_chat.init(system_prompt)
-	local thread = {
-		system = system_prompt
-		tools = tools
-		messages = {nil}
-	}
-	return json_string(thread)
-end
-
-function Ai_chat.has_messages(thread)
-	thread = json_parse(thread)
-	return #thread.messages > 0
-end
-
-local function chit_chat(thread)
-	thread = deep_copy(thread)
-	local messages = thread.messages or error()
-	for _, message in ipairs(messages) do
-		local content = message.content or error()
-		if type(content) == "string" then
-			content = {{
-				type = "text"
-				text = content
-			}}
-			message.content = content
-		end
-	end
-	local content = messages[#messages].content or error()
-	content[#content].cache_control = { type = "ephemeral" }
-	return claude_chat(thread)
-end
-
-local function ask(thread)
-	local messages = thread.messages or error
---[=[
-	messages[#messages+1] = {
-		role = "assistant"
-		content = [[
-hello
-]]
-	}
-	Thread.sleep(2000)
-	if true then
-		return
-	end
---]=]
-	-- logger.info(json_string(thread))
-	local resultJson = chit_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
-	}
-	local stop_reason = result.stop_reason or error()
-	if stop_reason == "end_turn" then
-		-- ok
-	elseif stop_reason == "tool_use" then
-		local response = {nil}
-		for _, part in ipairs(content) do
-			if part.type == "tool_use" then
-				local f = functions[part.name] or error()
-				local input = part.input or error()
-				response[#response+1] = {
-					type = "tool_result"
-					tool_use_id = part.id or error()
-					content = f.fn(input)
-				}
-			end
-		end
-		messages[#messages+1] = {
-			role = "user"
-			content = response
-		}
-		ask(thread)
-	else
-		error(stop_reason)
-	end
-end
-
-function Ai_chat.add(thread,input)
-	thread = json_parse(thread)
-	local messages = thread.messages or error
-	messages[#messages+1] = {
-		role = "user"
-		content = input
-	}
-	return json_string(thread)
-end
-
-function Ai_chat.respond(thread)
-	thread = json_parse(thread)
-	ask(thread)
-	return json_string(thread)
-end
-
-return Ai_chat
diff -r b96cf27e719d -r 3c60bc17403e src/lib/ai/claude/Claude.luan
--- a/src/lib/ai/claude/Claude.luan	Thu Aug 28 14:36:03 2025 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-local Luan = require "luan:Luan.luan"
-local error = Luan.error
-local ipairs = Luan.ipairs or error()
-local type = Luan.type or error()
-local Io = require "luan:Io.luan"
-local uri = Io.uri or error()
-local Parsers = require "luan:Parsers.luan"
-local json_string = Parsers.json_string or error()
-local Config = require "site:/private/Config.luan"
-local Logging = require "luan:logging/Logging.luan"
-local logger = Logging.logger "claude/Claude"
-
-
--- https://docs.anthropic.com/
-
-local Claude = {}
-
-local url = "https://api.anthropic.com/v1/messages"
-local headers = {
-	["x-api-key"] = Config.claude.key or error()
-	["anthropic-version"] = "2023-06-01"
-	["Content-Type"] = "application/json"
-}
-local model = "claude-sonnet-4-0"
-local max_tokens = 8192
-
-function Claude.chat(thread)
-	thread.model = thread.model or model
-	thread.max_tokens = thread.max_tokens or max_tokens
-	local options = {
-		method = "POST"
-		headers = headers
-		content = json_string(thread)
-	}
-	try
-		local response = uri(url,options).read_text()
-		return response
-	catch e
-		logger.error(options.content)
-		e.throw()
-	end
-end
-
-return Claude
diff -r b96cf27e719d -r 3c60bc17403e src/lib/ai/claude/Translator.luan
--- a/src/lib/ai/claude/Translator.luan	Thu Aug 28 14:36:03 2025 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-local Luan = require "luan:Luan.luan"
-local error = Luan.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/Translator"
-
-
-local Translator = {}
-
-function Translator.translate(text,lang)
-	local thread = {
-		system = `%>
-Translate <%=lang%> in the text you get to English.
-Preserve formatting.
-The text may also contain English.  Just leave that unchanged.
-<%		`
-		messages = {{
-			role = "user"
-			content = text
-		}}
-		temperature = 0
-	}
-	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()
-	#content==1 or error()
-	content = content[1]
-	content.type == "text" or error()
-	return content.text or error()
-end
-
-return Translator
diff -r b96cf27e719d -r 3c60bc17403e src/lib/claude/Ai_chat.luan
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/claude/Ai_chat.luan	Sun Aug 31 17:40:20 2025 -0600
@@ -0,0 +1,255 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local ipairs = Luan.ipairs or error()
+local pairs = Luan.pairs 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 Thread = require "luan:Thread.luan"
+local Claude = require "site:/lib/claude/Claude.luan"
+local claude_chat = Claude.chat or error()
+local Utils = require "site:/lib/Utils.luan"
+local deep_copy = Utils.deep_copy 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)
+	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(assistant_controls,thread,old_thread)
+	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)
+%>
+			<div role="<%=role%>" msg="<%=i%>">
+				<h3><%=who%></h3>
+				<div message markdown><%=text%></div>
+<%			if role=="assistant" then %>
+<%=				assistant_controls %>
+<%			end %>
+			</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 function get_chat(chat_id)
+	local Chat = require "site:/lib/Chat.luan"
+	local User = require "site:/lib/User.luan"
+	local chat = Chat.get_by_id(chat_id) or error()
+	local user = User.current()
+	local is_owner = user ~= nil and user.id == chat.user_id
+	is_owner or not chat.is_private or error "private"
+	return chat
+end
+
+local functions = {
+	get_chat = {
+		tool = {
+			description = "Get the contents of a chat/thread with Claude on this website.  The contents will be JSON in the format of the Claude API."
+			input_schema = {
+				type = "object"
+				properties = {
+					chat_id = {
+						description = "The ID of the chat"
+						type = "integer"
+					}
+				}
+			}
+		}
+		fn = function(input)
+			local chat_id = input.chat_id or error()
+			local chat = get_chat(chat_id)
+			return chat.ai_thread or error()
+		end
+	}
+	get_tts_instructions = {
+		tool = {
+			description = "Get the text-to-speech instructions of a chat/thread on this website.  These instructions are passed to OpenAI.  If there are no instructions, the empty string is returned."
+			input_schema = {
+				type = "object"
+				properties = {
+					chat_id = {
+						description = "The ID of the chat"
+						type = "integer"
+					}
+				}
+			}
+		}
+		fn = function(input)
+			local chat_id = input.chat_id or error()
+			local chat = get_chat(chat_id)
+			return chat.tts_instructions or error()
+		end
+	}
+	get_stt_prompt = {
+		tool = {
+			description = "Get the speech-to-text prompt of a chat/thread on this website.  This prompt is passed to OpenAI.  If there is no prompt, the empty string is returned."
+			input_schema = {
+				type = "object"
+				properties = {
+					chat_id = {
+						description = "The ID of the chat"
+						type = "integer"
+					}
+				}
+			}
+		}
+		fn = function(input)
+			local chat_id = input.chat_id or error()
+			local chat = get_chat(chat_id)
+			return chat.stt_prompt or error()
+		end
+	}
+}
+local tools = {nil}
+for name, f in pairs(functions) do
+	f.name = name
+	f.tool.name = name
+	tools[#tools+1] = f.tool
+end
+
+function Ai_chat.init(system_prompt)
+	local thread = {
+		system = system_prompt
+		tools = tools
+		messages = {nil}
+	}
+	return json_string(thread)
+end
+
+function Ai_chat.has_messages(thread)
+	thread = json_parse(thread)
+	return #thread.messages > 0
+end
+
+local function chit_chat(thread)
+	thread = deep_copy(thread)
+	local messages = thread.messages or error()
+	for _, message in ipairs(messages) do
+		local content = message.content or error()
+		if type(content) == "string" then
+			content = {{
+				type = "text"
+				text = content
+			}}
+			message.content = content
+		end
+	end
+	local content = messages[#messages].content or error()
+	content[#content].cache_control = { type = "ephemeral" }
+	return claude_chat(thread)
+end
+
+local function ask(thread)
+	local messages = thread.messages or error
+--[=[
+	messages[#messages+1] = {
+		role = "assistant"
+		content = [[
+hello
+]]
+	}
+	Thread.sleep(2000)
+	if true then
+		return
+	end
+--]=]
+	-- logger.info(json_string(thread))
+	local resultJson = chit_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
+	}
+	local stop_reason = result.stop_reason or error()
+	if stop_reason == "end_turn" then
+		-- ok
+	elseif stop_reason == "tool_use" then
+		local response = {nil}
+		for _, part in ipairs(content) do
+			if part.type == "tool_use" then
+				local f = functions[part.name] or error()
+				local input = part.input or error()
+				response[#response+1] = {
+					type = "tool_result"
+					tool_use_id = part.id or error()
+					content = f.fn(input)
+				}
+			end
+		end
+		messages[#messages+1] = {
+			role = "user"
+			content = response
+		}
+		ask(thread)
+	else
+		error(stop_reason)
+	end
+end
+
+function Ai_chat.add(thread,input)
+	thread = json_parse(thread)
+	local messages = thread.messages or error
+	messages[#messages+1] = {
+		role = "user"
+		content = input
+	}
+	return json_string(thread)
+end
+
+function Ai_chat.respond(thread)
+	thread = json_parse(thread)
+	ask(thread)
+	return json_string(thread)
+end
+
+return Ai_chat
diff -r b96cf27e719d -r 3c60bc17403e src/lib/claude/Claude.luan
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/claude/Claude.luan	Sun Aug 31 17:40:20 2025 -0600
@@ -0,0 +1,44 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local ipairs = Luan.ipairs or error()
+local type = Luan.type or error()
+local Io = require "luan:Io.luan"
+local uri = Io.uri or error()
+local Parsers = require "luan:Parsers.luan"
+local json_string = Parsers.json_string or error()
+local Config = require "site:/private/Config.luan"
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "claude/Claude"
+
+
+-- https://docs.anthropic.com/
+
+local Claude = {}
+
+local url = "https://api.anthropic.com/v1/messages"
+local headers = {
+	["x-api-key"] = Config.claude.key or error()
+	["anthropic-version"] = "2023-06-01"
+	["Content-Type"] = "application/json"
+}
+local model = "claude-sonnet-4-0"
+local max_tokens = 8192
+
+function Claude.chat(thread)
+	thread.model = thread.model or model
+	thread.max_tokens = thread.max_tokens or max_tokens
+	local options = {
+		method = "POST"
+		headers = headers
+		content = json_string(thread)
+	}
+	try
+		local response = uri(url,options).read_text()
+		return response
+	catch e
+		logger.error(options.content)
+		e.throw()
+	end
+end
+
+return Claude
diff -r b96cf27e719d -r 3c60bc17403e src/lib/claude/Translator.luan
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/claude/Translator.luan	Sun Aug 31 17:40:20 2025 -0600
@@ -0,0 +1,40 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.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/claude/Claude.luan"
+local claude_chat = Claude.chat or error()
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "claude/Translator"
+
+
+local Translator = {}
+
+function Translator.translate(text,lang)
+	local thread = {
+		system = `%>
+Translate <%=lang%> in the text you get to English.
+Preserve formatting.
+The text may also contain English.  Just leave that unchanged.
+<%		`
+		messages = {{
+			role = "user"
+			content = text
+		}}
+		temperature = 0
+	}
+	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()
+	#content==1 or error()
+	content = content[1]
+	content.type == "text" or error()
+	return content.text or error()
+end
+
+return Translator
diff -r b96cf27e719d -r 3c60bc17403e src/new_chat.red.luan
--- a/src/new_chat.red.luan	Thu Aug 28 14:36:03 2025 -0600
+++ b/src/new_chat.red.luan	Sun Aug 31 17:40:20 2025 -0600
@@ -6,7 +6,7 @@
 local Chat = require "site:/lib/Chat.luan"
 local Course = require "site:/lib/Course.luan"
 local get_course_by_id = Course.get_by_id or error()
-local Ai_chat = require "site:/lib/ai/claude/Ai_chat.luan"
+local Ai_chat = require "site:/lib/claude/Ai_chat.luan"
 local ai_init = Ai_chat.init or error()
 
 
diff -r b96cf27e719d -r 3c60bc17403e src/translate.js.luan
--- a/src/translate.js.luan	Thu Aug 28 14:36:03 2025 -0600
+++ b/src/translate.js.luan	Sun Aug 31 17:40:20 2025 -0600
@@ -6,7 +6,7 @@
 local Http = require "luan:http/Http.luan"
 local Shared = require "site:/lib/Shared.luan"
 local languages = Shared.languages or error()
-local Translator = require "site:/lib/ai/claude/Translator.luan"
+local Translator = require "site:/lib/claude/Translator.luan"
 local translate = Translator.translate or error()