Mercurial Hosting > lang
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
--- 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"
--- 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
--- 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
--- 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
--- 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
--- /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
--- /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
--- /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
--- 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()
--- 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()