changeset 1938:bd00b36380d9 default tip

ai options
author Franklin Schmidt <fschmidt@gmail.com>
date Fri, 16 May 2025 17:20:29 -0600 (20 hours ago)
parents d44659709959
children
files src/luan/modules/ai/Ai.luan src/luan/modules/ai/Site_translator.luan src/luan/modules/ai/claude/Claude.luan src/luan/modules/ai/claude/Translator.luan src/luan/modules/ai/gpt/Gpt.luan src/luan/modules/ai/gpt/Translator.luan src/luan/modules/gpt/Gpt.luan src/luan/modules/gpt/Site_translator.luan src/luan/modules/gpt/Translator.luan website/src/blasma.html.luan website/src/diff.html.luan website/src/docs.html.luan website/src/goodjava.html.luan website/src/hosting.html.luan website/src/index.html.luan website/src/install.html.luan website/src/learn.html.luan website/src/lib/Shared.luan website/src/manual.html.luan website/src/pil.html.luan website/src/scripting.html.luan website/src/support.html.luan website/src/translating.html.luan website/src/tutorial.html.luan website/src/why.html.luan
diffstat 25 files changed, 379 insertions(+), 250 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/ai/Ai.luan	Fri May 16 17:20:29 2025 -0600
@@ -0,0 +1,22 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "Ai"
+
+
+local Ai = {}
+
+local ai
+
+function Ai.set_ai(new_ai)
+	ai = new_ai
+	local Private = require "site:/private/lib/Private.luan"
+	Ai.key = Private[ai].key or error()
+end
+
+function Ai.require_ai(file)
+	ai or error "ai not set"
+	return require("luan:ai/"..ai.."/"..file)
+end
+
+return Ai
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/ai/Site_translator.luan	Fri May 16 17:20:29 2025 -0600
@@ -0,0 +1,135 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local get_local_only = Luan.get_local_only or error()
+local set_local_only = Luan.set_local_only or error()
+local String = require "luan:String.luan"
+local substring = String.sub or error()
+local regex = String.regex or error()
+local Io = require "luan:Io.luan"
+local uri = Io.uri or error()
+local Thread = require "luan:Thread.luan"
+local new_future = Thread.new_future or error()
+local global_callable = Thread.global_callable or error()
+local Package = require "luan:Package.luan"
+local load = Package.load or error()
+local Http = require "luan:http/Http.luan"
+local Ai = require "luan:ai/Ai.luan"
+local require_ai = Ai.require_ai or error()
+local Translator = require_ai "Translator.luan"
+local translate_html = Translator.translate_html or error()
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "Site_translator"
+
+
+local Site_translator = {}
+
+local function get_lang()
+	return get_local_only(Site_translator,"lang") or "en"
+end
+Site_translator.get_lang = get_lang
+
+local function set_lang(lang)
+	set_local_only(Site_translator,"lang",lang)
+end
+
+local function is_translated()
+	return get_local_only(Site_translator,"translated") or false
+end
+
+function Site_translator.set_translated()
+	set_local_only(Site_translator,"translated",true)
+end
+
+function Site_translator.text_writer()
+	if get_lang() == "en" then
+		return Http.response.text_writer()
+	end
+	local buf = uri("string:")
+	set_local_only(Site_translator,"buf",buf)
+	local text_writer = buf.text_writer()
+	set_local_only(Site_translator,"text_writer",text_writer)
+	return text_writer
+end
+
+local function read_buf()
+	local text_writer = get_local_only(Site_translator,"text_writer")
+	text_writer.close()
+	local buf = get_local_only(Site_translator,"buf")
+	return buf.read_text()
+end
+
+local lang_ptn = regex[[^/[a-z]{2}/]]
+
+Site_translator.languages = {
+	en = "English"
+	es = "Spanish"
+	ja = "Japanese"
+	pt = "Portuguese"
+}
+
+local function init_futures()
+	local fns = {}
+	local futures = {}
+	function fns.get_future(path,fn)
+		local future = futures[path]
+		if future == nil then
+			future = new_future(fn)
+			futures[path] = future
+		end
+		return future
+	end
+	function fns.remove_future(path)
+		futures[path] = nil
+	end
+	return fns
+end
+
+local function get_page_future(global,path,fn,lang)
+	local function get_html()
+		set_lang(lang)
+		fn()
+		local html = read_buf()
+		local language = Site_translator.languages[lang] or error(lang)
+		if not is_translated() then
+			html = translate_html(html,language,false)
+		end
+		return html
+	end
+	return global.get_future(path,get_html)
+end
+
+function Site_translator.not_found_handler()
+	local request = Http.request
+	local path = request.path
+	if not lang_ptn.matches(path) then
+		return false
+	end
+	local lang = substring(path,2,3)
+	local s = substring(path,4)
+	if s == "/" then
+		s = "/index.html"
+		request.set_path( path.."index.html" )
+	end
+	local fn = load("site:"..s..".luan")
+	if fn == false then
+		return false
+	end
+	local global = global_callable("Shared.futures",init_futures)
+	local future = get_page_future(global,path,fn,lang)
+	if not future.is_done() then
+		path = "/"..lang.."/translating.html"
+		local translating = require "site:/translating.html.luan"
+		future = get_page_future(global,path,translating,lang)
+	end
+	local html
+	try
+		html = future.get()
+	catch e
+		global.remove_future(path)
+		e.throw()
+	end
+	Http.response.text_writer().write(html)
+	return true
+end
+
+return Site_translator
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/ai/claude/Claude.luan	Fri May 16 17:20:29 2025 -0600
@@ -0,0 +1,34 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.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 Ai = require "luan:ai/Ai.luan"
+
+
+local Claude = {}
+
+local url = "https://api.anthropic.com/v1/messages"
+local model = "claude-3-7-sonnet-latest"
+local max_tokens = 20000
+
+function Claude.chat(content)
+	local headers = {
+		["x-api-key"] = Ai.key or error "Ai.key not set"
+		["anthropic-version"] = "2023-06-01"
+		["Content-Type"] = "application/json"
+	}
+	content.model = content.model or model
+	content.max_tokens = content.max_tokens or max_tokens
+	local options = {
+		method = "POST"
+		headers = headers
+		content = json_string(content)
+		time_out = 300000
+	}
+	local response = uri(url,options).read_text()
+	return response
+end
+
+return Claude
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/ai/claude/Translator.luan	Fri May 16 17:20:29 2025 -0600
@@ -0,0 +1,64 @@
+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 Thread = require "luan:Thread.luan"
+local sleep = Thread.sleep or error()
+local Claude = require "luan:ai/claude/Claude.luan"
+local chat = Claude.chat or error()
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "claude/Translator"
+
+
+local Translator = {}
+
+local fn_name = "translated_html"
+
+local input_schema = {
+	type = "object"
+	properties = {
+		translated_html = {
+			description = "The translated HTML"
+			type = "string"
+			format = "html"
+		}
+	}
+}
+
+-- must set Translator.prompt
+
+function Translator.translate_html(html,language,is_serial)
+	logger.info("translate_html "..#html)
+	local prompt = Translator.prompt or error "Translator.prompt not set"
+	local resultJson = chat{
+		tools = {{
+			name = fn_name
+			description = "Report the translated HTML"
+			input_schema = input_schema
+		}}
+		tool_choice = { name=fn_name, type="tool" }
+		messages = {{
+			role = "user"
+			content = `prompt(html,language)`
+		}}
+		temperature = 0
+	}
+	local result = json_parse(resultJson)
+	 logger.info(json_string(result))
+	result.stop_reason == "tool_use" or error(result.stop_reason)
+	if is_serial and result.usage.output_tokens > 10000 then
+		logger.info("start sleep")
+		sleep(30000)
+		logger.info("end sleep")
+	end
+	local content = result.content or error()
+	#content == 1 or error()
+	content = content[1]
+	content.type == "tool_use" or error()
+	content.name == fn_name or error()
+	local input = content.input or error()
+	return input.translated_html or error()
+end
+
+return Translator
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/ai/gpt/Gpt.luan	Fri May 16 17:20:29 2025 -0600
@@ -0,0 +1,35 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local Parsers = require "luan:Parsers.luan"
+local json_string = Parsers.json_string or error()
+local Io = require "luan:Io.luan"
+local uri = Io.uri or error()
+local Ai = require "luan:ai/Ai.luan"
+
+
+local Gpt = {}
+
+-- must set Gpt.key
+
+Gpt.model = "gpt-4o"
+
+local base_url = "https://api.openai.com/v1"
+
+function Gpt.chat(content)
+	content.model = content.model or Gpt.model
+	local url = base_url.."/responses"
+	local key = Ai.key or error "Ai.key not set"
+	local options = {
+		method = "POST"
+		headers = {
+			Authorization = "Bearer "..key
+			["Content-Type"] = "application/json"
+		}
+		content = json_string(content)
+		time_out = 300000
+	}
+	local response = uri(url,options).read_text()
+	return response
+end
+
+return Gpt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/ai/gpt/Translator.luan	Fri May 16 17:20:29 2025 -0600
@@ -0,0 +1,53 @@
+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 Gpt = require "luan:ai/gpt/Gpt.luan"
+local chat = Gpt.chat or error()
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "gpt/Translator"
+
+
+local Translator = {}
+
+local fn_schema = {
+	type = "function"
+	name = "translated_html"
+	description = "Report the translated HTML"
+	parameters = {
+		type = "object"
+		properties = {
+			translated_html = {
+				type = "string"
+				format = "html"
+				description = "The translated HTML"
+			}
+		}
+	}
+}
+
+-- must set Translator.prompt
+
+function Translator.translate_html(html,language,is_serial)
+	local prompt = Translator.prompt or error "Translator.prompt not set"
+	local resultJson = chat{
+		input = `prompt(html,language)`
+		tools = {fn_schema}
+		temperature = 0
+		store = false
+	}
+	--print(resultJson)
+	local result = json_parse(resultJson)
+	result = result.output or error()
+	#result==1 or error(resultJson)
+	result = result[1]
+	result.type=="function_call" or error(resultJson)
+	result.status=="completed" or error(resultJson)
+	result.name==fn_schema.name or error()
+	local arguments = json_parse(result.arguments)
+	local translated = arguments.translated_html or error()
+	--print(translated)
+	return translated
+end
+
+return Translator
--- a/src/luan/modules/gpt/Gpt.luan	Thu May 15 19:12:05 2025 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-local Luan = require "luan:Luan.luan"
-local error = Luan.error
-local Parsers = require "luan:Parsers.luan"
-local json_string = Parsers.json_string or error()
-local Io = require "luan:Io.luan"
-local uri = Io.uri or error()
-
-
-local Gpt = {}
-
--- must set Gpt.key
-
-Gpt.model = "gpt-4o"
-
-local base_url = "https://api.openai.com/v1"
-
-function Gpt.chat(content)
-	content.model = content.model or Gpt.model
-	local url = base_url.."/responses"
-	local key = Gpt.key or error "Gpt.key not set"
-	local options = {
-		method = "POST"
-		headers = {
-			Authorization = "Bearer "..key
-			["Content-Type"] = "application/json"
-		}
-		content = json_string(content)
-		time_out = 300000
-	}
-	local response = uri(url,options).read_text()
-	return response
-end
-
-return Gpt
--- a/src/luan/modules/gpt/Site_translator.luan	Thu May 15 19:12:05 2025 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-local Luan = require "luan:Luan.luan"
-local error = Luan.error
-local get_local_only = Luan.get_local_only or error()
-local set_local_only = Luan.set_local_only or error()
-local String = require "luan:String.luan"
-local substring = String.sub or error()
-local regex = String.regex or error()
-local Io = require "luan:Io.luan"
-local uri = Io.uri or error()
-local Thread = require "luan:Thread.luan"
-local new_future = Thread.new_future or error()
-local global_callable = Thread.global_callable or error()
-local Package = require "luan:Package.luan"
-local load = Package.load or error()
-local Http = require "luan:http/Http.luan"
-local Gpt = require "luan:gpt/Gpt.luan"
-local Private = load "site:/private/lib/Private.luan"
-Gpt.key = Private and Private.gpt_key
-local Translator = require "luan:gpt/Translator.luan"
-local translate_html = Translator.translate_html or error()
-
-
-local Site_translator = {}
-
-local function get_lang()
-	return get_local_only(Site_translator,"lang") or "en"
-end
-Site_translator.get_lang = get_lang
-
-local function set_lang(lang)
-	set_local_only(Site_translator,"lang",lang)
-end
-
-local function is_translated()
-	return get_local_only(Site_translator,"translated") or false
-end
-
-function Site_translator.set_translated()
-	set_local_only(Site_translator,"translated",true)
-end
-
-function Site_translator.text_writer()
-	if get_lang() == "en" then
-		return Http.response.text_writer()
-	end
-	local buf = uri("string:")
-	set_local_only(Site_translator,"buf",buf)
-	local text_writer = buf.text_writer()
-	set_local_only(Site_translator,"text_writer",text_writer)
-	return text_writer
-end
-
-local function read_buf()
-	local text_writer = get_local_only(Site_translator,"text_writer")
-	text_writer.close()
-	local buf = get_local_only(Site_translator,"buf")
-	return buf.read_text()
-end
-
-local lang_ptn = regex[[^/[a-z]{2}/]]
-
-Site_translator.languages = {
-	en = "English"
-	es = "Spanish"
-	ja = "Japanese"
-	pt = "Portuguese"
-}
-
-local function init_futures()
-	local fns = {}
-	local futures = {}
-	function fns.get_future(path,fn)
-		local future = futures[path]
-		if future == nil then
-			future = new_future(fn)
-			futures[path] = future
-		end
-		return future
-	end
-	return fns
-end
-
-local function get_page_future(path,fn,lang)
-	local function get_html()
-		set_lang(lang)
-		fn()
-		local html = read_buf()
-		local language = Site_translator.languages[lang] or error(lang)
-		if not is_translated() then
-			html = translate_html(html,language)
-		end
-		return html
-	end
-	local get_future = global_callable("Shared.futures",init_futures).get_future
-	return get_future(path,get_html)
-end
-
-function Site_translator.not_found_handler()
-	local request = Http.request
-	local path = request.path
-	if not lang_ptn.matches(path) then
-		return false
-	end
-	local lang = substring(path,2,3)
-	local s = substring(path,4)
-	if s == "/" then
-		s = "/index.html"
-		request.set_path( path.."index.html" )
-	end
-	local fn = load("site:"..s..".luan")
-	if fn == false then
-		return false
-	end
-	local future = get_page_future(path,fn,lang)
-	if not future.is_done() then
-		path = "/"..lang.."/translating.html"
-		local translating = require "site:/translating.html.luan"
-		future = get_page_future(path,translating,lang)
-	end
-	local html = future.get()
-	Http.response.text_writer().write(html)
-	return true
-end
-
-return Site_translator
--- a/src/luan/modules/gpt/Translator.luan	Thu May 15 19:12:05 2025 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +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 Gpt = require "luan:gpt/Gpt.luan"
-local chat = Gpt.chat or error()
-local Logging = require "luan:logging/Logging.luan"
-local logger = Logging.logger "gpt/Translator"
-
-
-local Translator = {}
-
-local fn_schema = {
-	type = "function"
-	name = "translated_html"
-	description = "Report the translated HTML"
-	parameters = {
-		type = "object"
-		properties = {
-			translated_html = {
-				type = "string"
-				format = "html"
-				description = "The translated HTML"
-			}
-		}
-	}
-}
-
-function Translator.prompt(html,language)
-%>
-Please translate the HTML below delimited by triple quotes from English to <%=language%>.
-
-Note that the content is related to computer programming, so keep that in mind while translating.  The English word "library" refers to a programming library, not a place to borrow books, so translate to the word in the target language that means programming library.  So for Spanish, use "librería".
-
-Don't translate file names.
-
-"""
-<%=html%>
-"""
-<%
-end
-
-function Translator.translate_html(html,language)
-	local resultJson = chat{
-		input = `Translator.prompt(html,language)`
-		tools = {fn_schema}
-		temperature = 0
-		store = false
-	}
-	--print(resultJson)
-	local result = json_parse(resultJson)
-	result = result.output or error()
-	#result==1 or error(resultJson)
-	result = result[1]
-	result.type=="function_call" or error(resultJson)
-	result.status=="completed" or error(resultJson)
-	result.name==fn_schema.name or error()
-	local arguments = json_parse(result.arguments)
-	local translated = arguments.translated_html or error()
-	--print(translated)
-	return translated
-end
-
-return Translator
--- a/website/src/blasma.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/blasma.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
--- a/website/src/diff.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/diff.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
--- a/website/src/docs.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/docs.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
--- a/website/src/goodjava.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/goodjava.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
--- a/website/src/hosting.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/hosting.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
--- a/website/src/index.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/index.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
--- a/website/src/install.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/install.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
--- a/website/src/learn.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/learn.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
--- a/website/src/lib/Shared.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/lib/Shared.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,11 +1,16 @@
+local ai = "gpt"
+
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local ipairs = Luan.ipairs or error()
 local pairs = Luan.pairs or error()
 local Time = require "luan:Time.luan"
 local Http = require "luan:http/Http.luan"
-local Translator = require "luan:gpt/Translator.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Ai = require "luan:ai/Ai.luan"
+Ai.set_ai(ai)
+local require_ai = Ai.require_ai or error()
+local Translator = require_ai "Translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local languages = Site_translator.languages or error()
 local Logging = require "luan:logging/Logging.luan"
@@ -14,7 +19,8 @@
 
 local Shared = {}
 
-function Translator.prompt(html,language)
+if ai == "gpt" then
+	function Translator.prompt(html,language)
 %>
 Please translate the HTML below delimited by triple quotes from English to <%=language%>.
 
@@ -28,7 +34,8 @@
 <%=html%>
 """
 <%
-end
+	end
+else error(ai) end
 
 Http.not_found_handler = Site_translator.not_found_handler or error()
 
--- a/website/src/manual.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/manual.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -6,7 +6,7 @@
 local Table = require "luan:Table.luan"
 local concat = Table.concat or error()
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local languages = Site_translator.languages or error()
@@ -17,7 +17,9 @@
 local show_toc = Shared.show_toc or error()
 local show_content = Shared.show_content or error()
 local show_content_info = Shared.show_content_info or error()
-local Translator = require "luan:gpt/Translator.luan"
+local Ai = require "luan:ai/Ai.luan"
+local require_ai = Ai.require_ai or error()
+local Translator = require_ai "Translator.luan"
 local translate_html = Translator.translate_html or error()
 local Logging = require "luan:logging/Logging.luan"
 local logger = Logging.logger "manual.html"
@@ -3746,11 +3748,11 @@
 		html = replace( html, "<div placeholder></div>", `show_content(content,2)` )
 	else
 		local language = languages[lang] or error(lang)
-		html = translate_html(html,language)
+		html = translate_html(html,language,true)
 		local parts = {}
 		for id, info in pairs(content) do
 			local part = `show_content_info(id,info,2)`
-			part = translate_html(part,language)
+			part = translate_html(part,language,true)
 			parts[#parts+1] = part
 		end
 		html = replace( html, "<div placeholder></div>", concat(parts) )
--- a/website/src/pil.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/pil.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
--- a/website/src/scripting.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/scripting.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
--- a/website/src/support.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/support.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
--- a/website/src/translating.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/translating.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
--- a/website/src/tutorial.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/tutorial.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"
@@ -83,12 +83,12 @@
 return function()
 	Io.stdout = Http.response.text_writer()
 %&gt;
-&lt;!doctype html>
-&lt;html>
-	&lt;body>
+&lt;!doctype html&gt;
+&lt;html&gt;
+	&lt;body&gt;
 		Hello World
-	&lt;/body>
-&lt;/html>
+	&lt;/body&gt;
+&lt;/html&gt;
 &lt;%
 end
 </code>
--- a/website/src/why.html.luan	Thu May 15 19:12:05 2025 -0600
+++ b/website/src/why.html.luan	Fri May 16 17:20:29 2025 -0600
@@ -1,7 +1,7 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
 local Io = require "luan:Io.luan"
-local Site_translator = require "luan:gpt/Site_translator.luan"
+local Site_translator = require "luan:ai/Site_translator.luan"
 local get_lang = Site_translator.get_lang or error()
 local text_writer = Site_translator.text_writer or error()
 local Shared = require "site:/lib/Shared.luan"