# aardio 范例: 问 AI - aardio 编程助手



```aardio
import inet.url;
import win.ui;
import fonts.fontAwesome;
/*DSG{{*/
var winform = win.form(text="问 AI - aardio 编程助手";right=759;bottom=620;bgcolor=0xFAF9F8;border="none")
winform.add(
bk={cls="bk";text="问 AI - aardio 编程助手 / F1 助手";left=21;top=3;right=198;bottom=23;align="left";dl=1;dt=1;z=15};
bkPrompt={cls="plus";left=5;top=461;right=754;bottom=615;bgcolor=0xFFFFFF;border={radius=12};clip=1;clipBk=false;db=1;disabled=1;dl=1;dr=1;z=1};
bkTitle={cls="bk";left=0;top=0;right=764;bottom=27;bgcolor=0xDBDAD9;dl=1;dr=1;dt=1;forecolor=0x5C5B5B;linearGradient=0;z=2};
btnClear={cls="plus";text="清除";left=601;top=586;right=659;bottom=615;align="left";bgcolor=0xFFFFFF;color=0x3C3C3C;db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF014';notify=1;textPadding={left=25};z=5};
btnCopy={cls="plus";text="复制";left=315;top=586;right=379;bottom=615;align="left";bgcolor=0xFFFFFF;color=0x3C3C3C;db=1;disabled=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF0C5';notify=1;textPadding={left=25};z=10};
btnSearch={cls="plus";text="联网";left=17;top=586;right=78;bottom=615;align="left";bgcolor=0xFFFFFF;color=0x3C3C3C;db=1;dl=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF26B';notify=1;textPadding={left=25};z=16};
btnSend={cls="plus";text="问 AI";left=668;top=586;right=740;bottom=615;align="left";bgcolor=0xFFFFFF;color=0x3C3C3C;db=1;dr=1;font=LOGFONT(h=-13);iconColor=0xFF617B;iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF0AA';notify=1;textPadding={left=25};z=4};
btnSetting={cls="plus";text="设置";left=81;top=586;right=148;bottom=615;align="left";bgcolor=0xFFFFFF;color=0x3C3C3C;db=1;dl=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF013';notify=1;textPadding={left=25};z=6};
btnSnap={cls="plus";left=277;top=587;right=310;bottom=616;align="left";bgcolor=0xFFFFFF;color=0x3C3C3C;db=1;disabled=1;dr=1;font=LOGFONT(h=-13);iconStyle={font=LOGFONT(h=-13;name='FontAwesome')};iconText='\uF030';notify=1;textPadding={left=25};z=12};
chkFix={cls="plus";text="更正";left=388;top=586;right=448;bottom=615;align="left";bgcolor=0xFFFFFF;db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-14;name='FontAwesome')};iconText='\uF0C8 ';notify=1;textPadding={left=24};z=11};
cmbAgent={cls="combobox";left=151;top=590;right=275;bottom=612;db=1;dl=1;edge=1;items={};mode="dropdownlist";z=17};
editMaxTokens={cls="edit";left=530;top=590;right=573;bottom=613;align="right";db=1;dr=1;edge=1;z=8};
editPrompt={cls="richedit";left=8;top=466;right=751;bottom=580;autohscroll=false;bgcolor=0xFFFFFF;db=1;dl=1;dr=1;link=1;multiline=1;vscroll=1;z=3};
spinMaxTokens={cls="spin";left=574;top=591;right=594;bottom=613;db=1;dr=1;z=7};
splitter={cls="splitter";left=-7;top=457;right=758;bottom=462;bgcolor=0xD1D1D1;db=1;dl=1;dr=1;horz=1;z=13};
static={cls="static";text="回复长度：";left=455;top=586;right=521;bottom=615;align="right";bgcolor=0xFFFFFF;center=1;db=1;dr=1;z=9};
wndBrowser={cls="custom";text="自定义控件";left=5;top=27;right=753;bottom=455;ah=1;db=1;dl=1;dr=1;dt=1;z=14}
)
/*}}*/

//按钮外观样式
winform.btnClear.skin({
	color={
		active=0xFF00FF00;
		default=0xFF3C3C3C;
		disabled=0xFF999999;
		hover=0xFFFF0000		
	}
})

//按钮外观样式
winform.btnSend.skin({
	color={
		active=0xFF00FF00;
		default=0xFF3C3C3C;
		disabled=0xFF999999;
		hover=0xFF7B61FF		
	}
})

//按钮外观样式
winform.btnSetting.skin({
	color={
		active=0xFF00FF00;
		default=0xFF3C3C3C;
		disabled=0xFF999999;
		hover=0xFFFF0000		
	}
})

winform.btnSearch.skin({
	color={
		active=0xFF00FF00;
		default=0xFF3C3C3C;
		disabled=0xFF999999;
		hover=0xFFFF0000		
	}
})

winform.btnCopy.skin({
	color={
		active=0xFF00FF00;
		default=0xFF3C3C3C;
		disabled=0xFF999999;
		hover=0xFFFF0000		
	}
})

winform.btnSnap.skin({
	color={
		active=0xFF00FF00;
		default=0xFF3C3C3C;
		disabled=0xFF999999;
		hover=0xFFFF0000		
	}
})

winform.chkFix.skin({
	color={
		active=0xFF00FF00;
		default=0xFF000000;
		disabled=0xFF999999;
		hover=0xFFFF0000		
	};
	checked={
		iconText='\uF14A';	
		color={
			active=0xFF00FF00;
			default=0xFF000000;
			disabled=0xFF999999;
			hover=0xFFFF0000		
		};	
	}
})

import win.ui.simpleWindow;
win.ui.simpleWindow(winform);

winform.bkPrompt.directDrawBackgroundOnly();
winform.setIcon("~/examples/AI/assistant.ico");
winform.show();

//必须放在 mainForm.show() 后面（DPI 初始化完成），保证普屏/高分屏都至少调用一次
winform.afterDpiChanged = function(){ 
	winform.setIcon("~/examples/AI/assistant.ico"); 
}

//创建显示聊天消虑的 Web 浏览器窗口
import web.form.chat;
var wb = web.form.chat(winform.wndBrowser);

import fsys.table;
config = fsys.table(io.appData("aardio/ide/aiChat/~"))

if(!config.itemData) {
	config.itemNames={[1]="默认"};
	config.itemData={[1]={proxy="";temperature=0.4;key='\0\1\96';model="aardio";url="https://ai.aardio.com/api/v1/";systemPrompt="";msgLimit=15}};
	..table.assign(config,config.itemData[1]);
	config.selItem=1;
}

var aiChatConfig = {}
if(config.selItem){
	aiChatConfig = ..table.assign({},config.itemData[config.selItem] );
}

wb.enableKatex(aiChatConfig.katex);

//清除上下文
var resetMessages = function(){
	
	wb.clear(); 

	//自动生成 aardio 编程助手系统提示词
	wb.aardioSystem(aiChatConfig.systemPrompt);	
	import ide;
	var ideEnv = '## 当前运行环境\r\n- 当前 aardio 主版本号为：' + (ide.getConfig("version") || (_AARDIO_VERSION));
	ideEnv = ideEnv ++ '\r\n- 当前日期：' + time(str,"%Y年%m月%d日","chs");
	ideEnv = ideEnv ++ '\r\n- 当前对话进程执行文件名："' + ..io._exefile + '"';
	ideEnv = ideEnv ++ '\r\n- 当前操作系统："' + ..win.version.format() + '"';
	
	var projPath = ide.getProjectPath();
	if(!#projPath) return wb.context(ideEnv);
	
	import fsys.file;
	var file = fsys.file(projPath,"r");
	if(!file) return;
	
	if(file.size() > 20000){
		file.close();
		return;
	}
	
	var xml = file.readAll();
	
	if(string.indexOf(xml,"\node_modules\")){
		winform.msgboxErr("
请不要将 node_modules 目录添加到 aardio 工程。

aardio 工程的 Web 前端源码目录请设置【忽略目录】为 true 。
前端发布文件目录请设置【本地构建】为 true 。

否则将严重影响性能与 AI 相关功能。"); 
	}
	else {
		
		var project = '\r\n\r\n'+"
## aardio 源文件与工程文件

aardio 代码文件的后缀名为 `.aardio`，可包含 UTF-8 编码的源代码，也可以包含编译后的二进制代码。

aardio 工程文件的后缀名为 `.aproj`，其内容是 XML 格式的工程配置，也使用  UTF-8 编码 。  
"

		project = project + '\r\n\r\n**用户当前在 aardio 开发环境中打开的工程文件路径是: **"'+projPath+'"\r\n'
	 	
		project = project + '\r\n\r\n**用户当前打开的工程文件内容如下：**'
		
		project = project + '\r\n\r\n```xml\r\n' + ..string.removeBom(xml) +  '\r\n```\r\n\r\n'
		
		var desc = /*****
	
**工程文件各 XML 节点的作用与含义：**
	
- project 元素指定工程配置，并作为工程根目录包含其他 folder 或 file 元素。

    project 元素的属性 ui 指定图形界面。 

        * 如果 ui 为 "win" 则为图形界面发布后运行默认不显示控制台。
        * 如果 ui 为"console" 则为控制台程序发布后运行时默认显示控制台窗口。

    project 元素的属性 dstrip 指定是否移除调试符号。 

        * `dstrip="true"` 则发布后移除调试信息，生成的文件更小但错误信息会缺少调试信息（例如文件名行号）。

- folder 元素为工程中的虚拟目录

    如果 folder  的属性 embed 为 "true" 则该目录发布后嵌入 EXE 资源文件，aardio 中很多函数和库都自动支持这种嵌入资源而不需要额外修改代码。例如对于 `string.load("/res/test.txt")`，无论参数指定的文件是不是 EXE 资源文件函数的返回值都是一样的，这是 aardio 的一个主要特性。

    如果 folder 元素的属性 local 为 "true" 则表示这是一个本地目录（通常也是 Web  前端工程的发布目录），发布为 EXE 时将添加该目录下的所有文件。这种目录在工程中不显示子级文件或目录，右键菜单的『同步本地目录』也是无效的。 

    如果 folder 元素的属性 ignored 为 "true" 是指这个目录在发布时被忽略（ignored）。这种目录通常用来指向包含 Web 前端工程源码的目录，工程本身其实并不需要这些多余的目录，生成 EXE 时也会忽略这种目录。

- file 元素则表示添加到工程中的文件

    在工程根目录下只能有一个应用程序启动文件, 文件路径必须是 `main.aardio` 或以  `.main.aardio` 结束。除了启动文件，工程根目录只能包含 folder 元素。

*****/
	
		project = ideEnv ++ project + desc + '\r\n\r\n';
	
		var codePath = ide.getActiveDocPath();
		if(#codePath && ..string.endsWith(codePath,".aardio",true) ){
			project = project + '**用户当前正在编辑的文件为:** "'+codePath+'"\r\n'
		} 
	
		wb.context(project,"aardio-environment");
	
	}
}

resetMessages();

if(_ASK_AI_CONTEXT_PROMPT && _ASK_AI_USER_PROMPT){ 
	wb.context(_ASK_AI_CONTEXT_PROMPT,"aardio 开发环境");
	winform.editPrompt.text = _ASK_AI_USER_PROMPT;
}

winform.btnClear.oncommand = function(id,event){
	resetMessages();//清除聊天上下文
	winform.editPrompt.setFocus();
}

winform.splitter.origTop = winform.splitter.top;

import thread.event;
var eventStop = thread.event(,true);

import com.interface.ITaskbarList3;
var taskbar = _WIN7_LATER && com.interface.ITaskbarList3.Create();

//响应按键事件，输入用户提示词
winform.btnSend.oncommand = function(id,event){
	
	if(winform.btnSend.text == "停止"){
		eventStop.set();
		
		winform.btnSend.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}
		return;
	}
	
	var prompt = winform.editPrompt.text;
	if(!#prompt){
		wb.errorMessage("请先输入问题。")
		winform.editPrompt.setFocus();
		return;
	}
	
	var prompt,isErrMsgBox = ..string.replace(prompt,"\s*✅\s*返回代码编辑器按 F1 键 AI 纠错\s*-+\s*确定\s*-+\s*","");

	var tApiUrl = inet.url.split(aiChatConfig.url);
	if(!tApiUrl){
		wb.errorMessage(`错误的接口网址，;<a href="javascript:void(0)" onclick="javascript:external.updateApiKey()">点这里重新设置</a>`)
		winform.editPrompt.setFocus();
		return;
	}
	
	winform.btnSend.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}
	
	winform.btnClear.disabled = true; 
	winform.btnSnap.disabled = true;
	winform.chkFix.disabled = true;

	wb.chatMessages.limit = aiChatConfig.msgLimit; 
	
	var assistantMsg = wb.lastAssistantMessage();
	if(assistantMsg && winform.chkFix.checked){
		//Few-shot Learning
		assistantMsg.content = ide.aifix.markdown(assistantMsg.content,true);
	}
	
	//输入 AI 提示词
	wb.prompt( prompt );
	winform.editPrompt.text = "";
	if(isErrMsgBox){
		wb.body.insertAdjacentHTML("beforeEnd",`<div class="error-message">复制的错误信息缺少代码上下文，请在关闭错误对话框 30 秒内回到代码编辑器按 <kbd>F1</kbd> 键。</div>`);
	}

	if(string.indexOf(prompt,`"""选中代码"""`)){
	
	} 
	elseif(wb.aiSearched 
		|| (aiChatConfig.model && ( ..string.endsWith(aiChatConfig.model,":online") ||  ..string.startsWith(aiChatConfig.model,"aardio") ) ) 
	){
	
	}
	elseif( (config.search[["mode"]]=="exa" && config.search.exa )
		||(config.search[["mode"]]=="tavily" && config.search.tavily ) ){
			
		wb.showLoading("正在联网搜索");
		
		import web.turndown; 
		var ok = thread.invokeAndWait( function(wb,prompt,searchConfig){
				import web.rest.jsonClient;;
				
				if(searchConfig.mode=="exa"){
					//导入 Exa 索接口 
					var exaClient = web.rest.jsonClient(); 
					exaClient.setHeaders({ "x-api-key": searchConfig.exa.key} )
					var exa = exaClient.api("https://api.exa.ai/");
					
					//搜索
					var searchData,err = exa.search({
    					query:"aardio 编程语言文档 范例 " + prompt,
    					contents={text= true},
    					numResults: searchConfig.exa.count || 2, 
    					includeDomains: searchConfig.exa.includeDomains,
    					excludeDomains: !searchConfig.exa.includeDomains ? searchConfig.exa.excludeDomains : null,
    					type:"keyword" //一般 keyword 搜索就够了（价格低一些）
					}) 
					
					var ret  = searchData[["results"]]
					if(ret){
						wb.url(ret);	
						return true;
					}
					elseif(err){
						wb.errorMessage(err); 
					}		
				}
				elseif(searchConfig.mode=="tavily"){
					var http = web.rest.jsonClient();  
					http.setAuthToken(searchConfig.tavily.key); 
					var tavily = http.api("https://api.tavily.com");
  	
					//搜索
					var searchData,err = tavily.search({ 
    					query = "aardio 编程语言文档 范例 "  + prompt,
    					includeDomains = searchConfig.exa.includeDomains,
    					excludeDomains: !searchConfig.exa.includeDomains ? searchConfig.exa.excludeDomains : null,
    					max_results =  searchConfig.exa.count || 2 //返回的搜索结果数量，不必要太多，前两三条就可以了
					})
					
					//将搜索结果添加到系统提示词
					var ret = searchData[["results"]]
					if(ret){
						wb.url(ret);	
						return true;
					}
					elseif(err){
						wb.errorMessage(err);
					}	
				} 		
			},wb,prompt,config.search) 
			
			if(!ok){
				
				winform.btnSend.disabledText = null;
				winform.btnClear.disabled = false; 
				winform.btnSnap.disabled = false;
				winform.chkFix.disabled = false;
				
				return;
			}
			
			wb.aiSearched = true;
			wb.showLoading("正在思考")
	}

	aiChatConfig.maxTokens = winform.spinMaxTokens.pos;
	
	winform.splitter.splitAt(winform.splitter.origTop);
	
	eventStop.reset();
	
	var loading = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250' }
	winform.btnSend.disabledText = null;
	winform.btnSend.text = "停止" 
	
	//显示动画，但启用按钮
	winform.btnSend.reduce(loading,function(value,index){
		if(value) winform.btnSend.iconText = value;
		return 150;
	} )
	
	if(taskbar) taskbar.SetProgressState(winform.hwnd,1/*_TBPF_INDETERMINATE*/);

	//创建多线程向服务端发送请求
	thread.invoke( 
		function(wb,aiChatConfig,eventStop){
			for(k,v in aiChatConfig){ 
				if(v==="")aiChatConfig[k] = null;
			} 
			
			if(!#aiChatConfig.key){
				aiChatConfig = table.assign(aiChatConfig,{  
					url = "https://ai.aardio.com/api/v1/";
					model = "aardio";
					temperature = 0.1;
				});	
			} 
			
			//导入调用 HTTP 接口的 REST 客户端
			import web.rest.aiChat;
			aiChatConfig.userAgent = "aardio-assistant/3.0(Windows NT "+ _WIN_VER_MAJOR +"."+_WIN_VER_MINOR+"; +https://www.aardio.com)";
			
			var client = web.rest.aiChat(aiChatConfig);
			client.referer = "https://aardio.com";
			client.setHeaders({ "X-Title":"aardio"});
   
			var ok,err = client.messages(wb.chatMessages,function(deltaText,deltaReasoning){
				if(eventStop.wait(0)){
					wb.assistant(null)
					return  false; 
				}
				
				if(#deltaReasoning){
					wb.showThinking(deltaReasoning);
					return;
				}
				
				wb.assistant(deltaText);
			} );
 
					
			if(err){
				//获取错误对象（解析 JSON 格式的错误信息）
				var errObject = client.lastResponseError()
				if(errObject[["error"]][["type"]] == "authentication_error" ){
					wb.errorMessage(`API 密钥错误！<a href="https://aardio.com/vip/">点这里获取密钥</a>，&nbsp;<a href="javascript:void(0)" onclick="javascript:external.updateApiKey()">点这里设置新密钥</a>`)
				}
				else {
					wb.errorMessage(err)
				}
			}  
			elseif(!ok){
				wb.errorMessage("错误代码：" + ( client.lastStatusCode : "未知 " + (client.lastResponseString() || "")))
			}
			
			if(ok){
				
				//在 AI 对话尾部增加复制 &#xF0C5;  不喜欢 &#xF088; 喜欢 &#xF164; 的反馈按钮
				wb.insertAdjacentHTML("beforeEnd", `
	<div style="color: #999">
<span style="cursor:pointer" title="复制代码" onclick="

var oldHTML = this.innerHTML;
var result = external.copyCode() ? '&amp;#xF00C;&lt;/span&gt; 已复制最长代码块' : '&amp;#xF00D;&lt;/span&gt; 无代码块';

this.innerHTML = '&lt;span style=&quot;font-family: &amp;#x27;FontAwesome&amp;#x27;;font-size: 16px;&quot;&gt;' + result;

var eleCopySpan = this;
setTimeout(function() { eleCopySpan.innerHTML = oldHTML; }, 1500);

"><span style="font-family: 'FontAwesome';font-size: 16px;">&#xF0C5;</span> 复制代码</span>
&nbsp;&nbsp;<span style="cursor:pointer" title="喜欢" onclick="external.like()"><span style="font-family: 'FontAwesome';font-size: 16px;">&#xF164;</span> 满意</span>
&nbsp;&nbsp;<span style="cursor:pointer" title="反馈此对话到 VIP 专属服务邮箱" onclick="external.dislike()"><span style="font-family: 'FontAwesome';font-size: 16px;">&#xF165;</span> 转人工</span>
	</div>
	`)
			}
			
			wb.onComplete(ok);
			 
		},wb,aiChatConfig,eventStop//将参数传入线程
	)
	
	winform.btnCopy.disabled = false;
}

//在 AI 回复结束后回调此函数
wb.onComplete = function(success){ 
	
	winform.btnSend.disabledText = null;
	winform.btnSend.reduce(false);
	
	winform.btnSend.text = "问 AI";
	winform.btnSend.iconText = '\uF0AA';
	
	winform.btnClear.disabled = false;
	winform.btnCopy.disabled = false;
	winform.btnSnap.disabled = false;
	winform.chkFix.disabled = false;
	winform.editPrompt.setFocus();
	
	if(taskbar) taskbar.SetProgressState(winform.hwnd,0/*_TBPF_NOPROGRESS*/);
	
	if(config.playSound){
		import sys.midiOut;
		if(success) sys.midiOut.play("changeInstrument(10), 3_, 5_, 1'_", "C4", 130);
		else sys.midiOut.play("changeInstrument(5), 2_", "C3", 100);
	}
}

//在 AI 回复结束以前回调此函数，自动修正 aardio 代码块中的常见幻觉错误
import ide.aifix;
wb.beforerWriteEnd = function(markdown){
	if(winform.chkFix.checked) { 
		return ide.aifix.markdown(markdown,true);
	}
	
	return markdown;
}

//导出 aardio 函数到网页 JavaScript 中。
wb.external = {
	updateApiKey = function(){
		winform.btnSetting.oncommand();
	} 
	copyCode = function(){
		var md = wb.lastAssistantMessage(true)
		if(!#md) return false;
	
		var found = false;
		var lastcode;
		for indent,_,code in string.gmatch(md,"!\N([ \t]*)(<```+>|<~~~+>)<aardio>(.+?)!\N\s*\2![^`\S]") { 
			
			if(#indent){ 
				text = string.replace(text,"\n+"+indent,'\n');
			}	
			
			if(#code < #lastcode){
				continue;
			}
			
			lastcode = code;
		
			if(winform.chkFix.checked) code = ide.aifix(code,true);
		    win.clip.write( code );
		    found = true;
		}
		
		return found;
			
	}
	like = function(){
		if(inet.url.split(aiChatConfig.url).host!="ai.aardio.com"){
			winform.msgboxErr("仅 VIP 专属接口可用！")
			return;
		}
		
		import web.rest.jsonLiteClient;
		var http = web.rest.jsonLiteClient();
		http.setAuthToken(aiChatConfig.key);
		
		var resp = http.post( "https://ai.aardio.com/api/v1/report/",{
			message = JSON.stringify(wb.chatMessages);
			like = true;
		})
			
		winform.msgbox( resp[["message"]] || "提交失败");	
	}
	dislike = function(){
	 
		if(inet.url.split(aiChatConfig.url).host!="ai.aardio.com"){
			winform.msgboxErr("仅 VIP 专属接口可用！")
			return;
		}
		
		import web.rest.jsonLiteClient;
		var http = web.rest.jsonLiteClient();
		http.setAuthToken(aiChatConfig.key);
		
		var resp = http.post( "https://ai.aardio.com/api/v1/report/",{
			message = JSON.stringify(wb.chatMessages);
			like = false;
		})
			
		winform.msgbox( resp[["message"]] || "提交失败");
	} 
}

import key;
import win.clip;
winform.btnCopy.oncommand = function(id,event){
	var md = wb.lastAssistantMessage(true)
	if(!#md) return winform.msgboxErr("回复内容为空！");
	
	if(key.getState("CTRL")){
		
		var found;
		var lastcode;
		
		for indent,_,code in string.gmatch(md,"!\N([ \t]*)(<```+>|<~~~+>)<aardio>(.+?)!\N\s*\2![^`\S]") { 
			
			if(#indent){ 
				text = string.replace(text,"\n+"+indent,'\n');
			}	
			
			if(#code < #lastcode){
				continue;
			}
			
			lastcode = code;
		
			if(winform.chkFix.checked) code = ide.aifix(code,true);
		    win.clip.write( code );
		    found = true;
		}
		
		if(!found){
			return winform.msgboxErr("没有找到代码块。");	
		} 
	}
	else{
		if(key.getState("SHIFT")) md = wb.getMarkdown();
	
		if(winform.chkFix.checked) md = ide.aifix.markdown(md,true);
		win.clip.write( md )
	}
	
	winform.btnCopy.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250';text=''} 
	thread.delay(800);
	winform.btnCopy.disabledText = null;
	
	winform.editPrompt.setFocus();
}

//设置接口地址与 API 令牌的窗口
winform.btnSetting.oncommand = function(id,event){

	..table.assign(config,{
		title = "aardio - 设置 aardio 编程助手 / F1 助手";
		f1Title = "设为 F1 键编码助手";
		hyperlink1Title = "VIP";
		f1MultiSelect = false;
		allowKatex = wb.documentMode>=11;
	});
		
	import web.rest.aiChat.settingForm;
	var frmSetting = web.rest.aiChat.settingForm(winform,config);
	
	frmSetting.onSelChange = function(selIndex,configItem){
		var currentSelIndex = winform.cmbAgent.selIndex;
		winform.cmbAgent.items = config.itemNames;
		
		if(currentSelIndex<#config.itemNames){
			winform.cmbAgent.selIndex = currentSelIndex;
		}
		else{
			winform.cmbAgent.selIndex = #config.itemNames;	
		}
	}
	
	frmSetting.onHyperlink = function(apiUrl,apiKey){
		
		if(!#apiKey){
			frmSetting.msgboxErr("请先输入接口密钥（ API key ）")
			frmSetting.editApiKey.setFocus();
			return;
		}
		
		import web.form;
		var frmWeb = win.form(text="aardio - VIP 会员中心" )
	
		var wb = web.form( frmWeb );
		wb.external = {
			openUrl = function(url){
				import process;
				process.openUrl(url);	
				frmWeb.close(true);
			} 
		}
		
		frmWeb.show(3/*_SW_MAXIMIZE*/);
		wb.post("https://ai.aardio.com/vip/",{apiKey=apiKey});	
	}
	
	if( frmSetting.doModal(winform) ){
		var configItem = config.selItem ? config.itemData[config.selItem] 
		if(configItem){
			
			aiChatConfig = table.assign({},configItem); 
			
			winform.cmbAgent.items = config.itemNames;
			winform.cmbAgent.selIndex = config.selItem; 
			
			if(!wb.started()){
 				if( !( _ASK_AI_SYSTEMP_PROMPT  && string.find(winform.editPrompt.text,`<"""选中代码""">|<"""报错信息""">`) )){
 					resetMessages();
 				} 
 			} 
		} 
	}
	
	winform.editPrompt.setFocus();
}

winform.chkFix.checked = true;
winform.chkFix.oncommand = function(id,event){
	var md = wb.getMarkdown();
	if(owner.checked){
		md = ide.aifix.markdown(md,true)
	}
	
	wb.write(md);
	
	winform.editPrompt.setFocus();
}

var tip = /*
- 在代码编辑器按 `F1` 键，无选区调用 AI 写代码，有选区打开问答或文档。
	* 建议在输入光标前用<code style="background: #f0f0f0; padding: 3px 6px; border-left: 4px solid #999; font-size: 1.0em;  color: #6A9955; ">//注释</code>说明需求，再按 `F1` 键。 
	* 运行报错后 30 秒内在代码编辑器按 F1 ，无选区则调用 AI 纠错。
	* 使用第三方 API 因无法接入专业版 aardio 知识库，生成的回复与代码质量会严重下降。
	* <a href="https://aardio.com/vip/">✨ 点此购买专业版密钥（API Key）</a>&nbsp;<a href="javascript:void(0)" onclick="javascript:external.updateApiKey()">⚒ 设置新密钥</a>&nbsp;<a href="https://aardio.com/vip/find-key/">💡 找回密钥</a>
- 左键 3 连击或按住 `Ctrl` 键单击任意代码块全选代码。
- 按住 `Ctrl` 键点底部 `复制` 按钮可复制代码块（默认选择最长代码块）。
- 按住 `Ctrl` 键点下面的 <image src="data:image/x-png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAiCAYAAAAkjjtxAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYcAAB2HAY/l8WUAAAFzSURBVFhH7ZY9roQwDIT3wpyFK3ABempKSi5ARUVDRUHDagpLfnYICcTWPomRRlptQvLJP0k+xz/TR/7x63qBrXULeNu2Y1mWqDHHQtnAwzAcVVUleZom+fljZQHP86ygrlw60lnAXdcpoCsjIyV1Cox0juP4xxImxXVdq3WelIoC3vf9aJpGbVzabdvKrZOkgPu+V4tbGdHOlQKWi1r6TpQfASMbODno7MXvnAy5AaPGY8fVuq7qm5BdgAGLxiQhqtT9ACWlQLsAI/UQoLGhHEdJkHB8yXFuc2BElxSrVd79cozbHJhAUL9yTJoUux3dgFEWckyaFLsh3YBTGooUKx1zYKSXFLu+eePF5pkDw3T+nkWZH3tXpeMCzKMMeEQTG8MoGYJNeUS5AMM85SGlwMJuwDCAcMtxARSXBd7Acn7IrsDcAEyF5C4CHLpurXxVWiEp4LPutzB/RKVKAUOA5t1f2lj7DiwUBP5lvcDWeoGt9QXF/vNj8+wS+wAAAABJRU5ErkJggg==" height="24"  style="display: inline-block; vertical-align: top;"> 按钮可截长屏到剪贴板。 
- 按住 `Ctrl+Enter` 可直接发送问题。
*/

wb.write(tip)

//默认设置输入框焦点
winform.editPrompt.setFocus();

winform.splitter.ltMin = 200;
winform.splitter.rbMin = 150;

var scrollbarHeight = ::User32.GetSystemMetrics(3/*_SM_CYHSCROLL*/)
winform.editPrompt.onOk = function(ctrl,alt,shift){ 
	if(ctrl){
		winform.btnSend.oncommand();
		return true; 
	} 
	
	var pt = ::POINT()
	::User32.GetCaretPos(pt) 
	
	var lineCount = winform.editPrompt.lineCount;
	var lineHeight = math.ceil(pt.x / lineCount + winform.dpiScale(5)); 
	  
	if(pt.y+(lineHeight+scrollbarHeight)*3>winform.editPrompt.height){  
 
		winform.wndBrowser.setRedraw(false)
		winform.splitter.splitMove(-lineHeight) 
		winform.wndBrowser.setRedraw(true) 
	}
}

//拆分界面
winform.splitter.split(winform.wndBrowser,{winform.bkPrompt,winform.editPrompt});

winform.editPrompt.enablePopMenu(function(){
	return { 
		
		{ '问 AI（发送）\tCtrl+Enter';  function(id){
			winform.btnSend.oncommand();
		}; 0};  
		
		{ /*分隔线*/ };
		{ '复制 aardio 专用提示词';  function(id){
			
			import win.clip;
			var prompt = ..string.removeBom(string.load("~\docs\guide\ide\system-prompt.md"));
			prompt = string.replace(prompt,"\#\#\s*aardio\s*作者联系方式.*$",'');
			
			var title  = "已复制 aardio 系统提示词（按住 Shift 键可增加语法速览） ";
			if(::User32.GetKeyState(0x10/*_VK_SHIFT*/)&0x8000){
				var qr = ..string.load("~\docs\guide\language\syntax-quick-ref.md");
				if(qr){
					prompt = prompt + '\r\n\r\n---\r\n\r\n' + qr;
					title  = "已复制 aardio 系统提示词 + 语法速览 ";
				}
			}
			
			var libs = ..string.removeBom(..string.load("~/lib/libs.txt"))
			if(libs){
				prompt = prompt + '\r\n\r\n---\r\n\r\naardio 全部可用库（内置库、标准库、扩展库）如下：' + libs + '\r\n\r\n以上库都可以用 import 语句导入，例如 `import JSON`。win.ui.ctrl 命名空间内的 custom,static,button,edit,richedit,plus,combobox,listbox,tab,progress,datetimepick,hotkey,listview,vlistview,treeview,checklist,trackbar,scrollbar,spin,calendar,thread,splitter,atlax,syslink,ipaddress,bk,bkplus,close,picturebox 等控件可按需自动导入（不需要再 import）';
			}
			
			win.clip.write(prompt)
			
			import string.gpt;
			import math.format;
			var tokens = string.gpt.tokens(prompt);
			tokens = math.format.size(tokens,"≈ %s%s tokens");
			winform.msgbox(title+'\n' + tokens);
		} }; 
		
		{ /*分隔线*/ };
		
		{ '导入对话';  function(id){
			import fsys.dlg;
			var path = fsys.dlg.open("*.jsonl|*.jsonl|*.json|*.json||","自 jsonl 或 json 文件导入对话");
			if(!path) return;
			
			wb.importChatMessages(path);
		}; winform.btnSend.text == "停止" ? 1/*_MF_GRAYED*/ : 0}; 
		
		{ "导出对话";  function(id){
			import fsys.dlg;
			var path = fsys.dlg.save("*.jsonl|*.jsonl|*.json|*.json||","导出对话到 *.jsonl");
			if(!path) return;
			
			var file,err = io.file(path,"w+b");
			if(!file) return winform.msgboxErr(err);
			 
			var chatMessages = wb.chatMessages;
			
			if(..string.endWith(path,".jsonl",true)){
				for(i=1;#chatMessages;1){
					var msg = chatMessages[i]
					var line  = JSON.stringify(msg,false,false);
					line = string.crlf(line,"")
					file.write(line,'\r\n');
				}
			}
			else {
				file.write((JSON.stringifyArray(chatMessages,true,false)));
			}
			
			file.close();
			
		}; #wb.chatMessages<3 ? 1/*_MF_GRAYED*/ : 0}; 
		 
		
		{ "编辑对话";  function(id){
		   
			ide.createProcess("~\examples\WebUI\web.view\OtherApps\json-editor.aardio",{
				_JSON_EDITOR_TEXT = JSON.stringifyArray(wb.chatMessages,true,false);
			});
			
		}; #wb.chatMessages<3 ? 1/*_MF_GRAYED*/ : 0}; 
		
		{ /*分隔线*/ };
		
		(win.getStyleEx(winform.hwnd, 8/*_WS_EX_TOPMOST*/) & 8)
		? ( { '取消置顶';  function(id){
			win.setTopmost(winform.hwnd,false)
		} } ) 
		: ( { '置顶窗口';  function(id){
			win.setTopmost(winform.hwnd)
		} } )
	}
})

wb.BeforeNavigate2 = function( pDisp, url, flags, targetFrame, postData, headers, cancel ) { 
 
    if(..string.match(url,"\.<@@jsonl@>|<@@json@>$") && io.exist(url) ){ 
        winform.setTimeout( 
        	function(){
        		wb.importChatMessages(url)
        	}
        );	
    } 
    else {
        
   		if(url[1]=='a'# && ..string.startsWith(url,"about:") ){ 
				url = ..string.replace(url,"^about\:<<\.\./>+>|/(<language\-reference/>|<library\-guide/>|<language\-reference/>|<guide/>)"
        			,"https://www.aardio.com/zh-cn/docs/\1"); 
    	}
        
		url = ..string.replace(url,"^(<@https://www.aardio.com/zh-cn/docs/library-guide/std/@>)(.+)$"
    		,function(dir,path){
        		path = ..string.replace(path,"\.html$",".md"); 
        		
        		var fullpath = io.joinpath("~/docs/library-guide/std",path);
        		if(!..io.exist(fullpath)){
        			return "https://www.aardio.com/zh-cn/docs/library-reference/"+path
        		}
    		} 
		);  
 		
		url = ..string.replace(url,"\.md$",".html"); 
		
		url = string.replace(url,"^<@https://www.aardio.com/zh-cn/docs/library-reference/@>(.+?)</_>?\.html$"
			,function(libPath){
				libPath = ..string.replace(libPath,"/",".");
				libPath = ..string.replace(libPath,"\.\.","."); 
				var libPath2 = ..io.libpath(libPath) 
				if(!libPath2){
					return "https://www.aardio.com/zh-cn/docs/?q="+libPath;
				}
				
				libPath2 = ..fsys.path.relative(libPath2,"~/lib",false); 
				libPath2 = ..string.replace(libPath2,"\.aardio$","");
				libPath2 = ..string.replace(libPath2,"\\","/");
				
				return "https://www.aardio.com/zh-cn/docs/library-reference/"+libPath2+".html";
			}
		)
		
    	..raw.execute(url);
    }
    
    return url, flags, targetFrame, postData, headers, true;
} 

winform.onDropFiles = function(files){
	var path = files[1];
	if(..string.match(path,"\.<@@jsonl@>|<@@json@>$")){
		wb.importChatMessages(path);
	}
}

wb.importChatMessages = function(path){
	var json = string.load(path);
	if(!#json){
		return winform.msgboxErr("无效对话数据")
	}
	
	var messages;
	
	if(..string.endsWith(path,".jsonl",true)){
		messages = JSON.ndParse(json);
	}
	else {
		messages = JSON.parse(json);
	}
	
	
	if(!#messages){
		return winform.msgboxErr("无效对话数据")
	}
			
	wb.clear();  
	wb.aiSearched = null;
	
	for(i=1;#messages;1){
		var msg = messages[i];
		
		if(type(msg.content)!="string"){
			 
			if(msg[["role"]]=="assistant"){
				var f = msg[["tool_calls"]][[1]][["function"]];
				if(f[["name"]] && f[["arguments"]]){
					wb.assistant("正在调用工具 "+f.name+ '，参数：\r\n\r\n```javascript\r\n'
					+ f.arguments + '\r\n```\r\n\r\n') 
					continue;
				}
				
			}
			
			if(msg[["role"]]!="user"){
				resetMessages();
				return winform.msgboxErr("对话数据格式错误，role 字段的值不是 user 则 content 字段必须是字符串！")
			}
			elseif(!..table.isArrayLike(msg.content)){
				return winform.msgboxErr("对话数据格式错误，content 字段只能是字符串或数组！")
			}
		}
		
		if(msg[["role"]]=="system"){
			wb.system(msg.content)
		}
		elseif(msg[["role"]]=="user"){
			wb.prompt(msg.content)
		}
		elseif(msg[["role"]]=="assistant"){
			
			var content = msg.content || "";
			if(winform.chkFix.checked){
				content = ide.aifix.markdown(content,true);
			}
			wb.assistant(content)
			wb.assistant(null)
		}
		else {
			//resetMessages();
			//return winform.msgboxErr("对话数据格式错误，role 字段必须是 system,user,assistant 之一。")
		} 
	} 	
}

global.onError = function( err,over ){ 
    if(!over){
        import debug;
        var stack = debug.traceback(,"调用栈",3);
        err = string.concat(err,stack);
    }
    
    if( _STUDIO_INVOKED ) {
    	import win;
    	win.msgboxErr(err);
    }
}

winform.spinMaxTokens.buddy = winform.editMaxTokens;
winform.spinMaxTokens.setRange(1,1024*16);
winform.spinMaxTokens.pos = config.maxTokens || 2048;
winform.spinMaxTokens.inc = 1024;

winform.beforeDestroy = function(){
    config.maxTokens = winform.spinMaxTokens.pos;
}


winform.btnSnap.oncommand = function(id,event){
	import fsys.dlg;
	import web.form.snap; 
	
	if(key.getState("CTRL")){
		winform.btnSnap.disabled = true;
		
		web.form.snap(wb,function(bmp){
				var hbmp = bmp.copyHandle();
				win.clip.writeBitmap(hbmp,true);
				return true;
		} );  
	}
	else{
		var path = fsys.dlg.save("*.jpg|*.jpg","AI 聊天助手 - 保存对话截图",,winform);
		winform.editPrompt.setFocus();
		
		if(!path) return;
		 
		winform.btnSnap.disabled = true;
		
		web.form.snap(wb,path); 
		winform.editPrompt.setFocus();
	}
	
	wb.doScript(`document.documentElement.scrollTop = document.documentElement.scrollHeight + 50;`);
 
	thread.delay(1000);
	winform.btnSnap.disabled = false;
	
	winform.editPrompt.setFocus();
}

winform.btnSearch.oncommand = function(id,event){

	var frmSearch = win.form(text="AI 联网搜索接口配置";right=852;bottom=434;bgcolor=0xFFFFFF;border="dialog frame";max=false)
	frmSearch.add(
	btnSave={cls="plus";text="保存 / 测试搜索";left=657;top=389;right=811;bottom=419;align="left";color=0x3C3C3C;db=1;dr=1;font=LOGFONT(h=-13);iconColor=5724159;iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF0AA';notify=1;textPadding={left=25};z=15};
	chkExa={cls="plus";text="启用 exa.ai 联网搜索";left=23;top=15;right=242;bottom=51;align="left";dl=1;dt=1;font=LOGFONT(h=-15);iconStyle={align="left";font=LOGFONT(h=-15;name='FontAwesome')};iconText='\uF0C8 ';notify=1;textPadding={left=24};z=2};
	chkTavily={cls="plus";text="启用 Tavily 联网搜索";left=23;top=111;right=234;bottom=142;align="left";db=1;dl=1;font=LOGFONT(h=-15);iconStyle={align="left";font=LOGFONT(h=-15;name='FontAwesome')};iconText='\uF0C8 ';notify=1;textPadding={left=24};z=3};
	editSearchCount={cls="edit";text="2";left=114;top=218;right=409;bottom=250;dl=1;dt=1;edge=1;multiline=1;num=1;z=7};
	editExaExcludeDomains={cls="edit";left=114;top=303;right=409;bottom=335;db=1;dl=1;dt=1;edge=1;multiline=1;z=11};
	editExaIncludeDomains={cls="edit";left=114;top=261;right=409;bottom=293;dl=1;dt=1;edge=1;multiline=1;z=9};
	editExaKey={cls="edit";left=114;top=64;right=409;bottom=96;dl=1;dt=1;edge=1;multiline=1;password=1;z=1};
	editQuery={cls="edit";left=46;top=391;right=630;bottom=423;db=1;dl=1;dr=1;edge=1;multiline=1;z=13};
	editResult={cls="richedit";left=427;top=23;right=836;bottom=378;db=1;dr=1;dt=1;edge=1;hscroll=1;link=1;multiline=1;vscroll=1;z=14};
	editTavilyKey={cls="edit";left=114;top=152;right=409;bottom=184;db=1;dl=1;edge=1;multiline=1;password=1;z=5};
	lbTavilyKey={cls="static";text="API key：";left=15;top=157;right=101;bottom=184;align="right";db=1;dl=1;transparent=1;z=6};
	lbSearchCount={cls="static";text="最大网页数：";left=-11;top=226;right=101;bottom=253;align="right";dl=1;dt=1;transparent=1;z=8};
	lbExaKey={cls="static";text="API key：";left=14;top=71;right=101;bottom=98;align="right";dl=1;dt=1;transparent=1;z=4};
	lbExcludeDomains={cls="static";text="排除域名：";left=34;top=311;right=101;bottom=338;align="right";db=1;dl=1;dt=1;transparent=1;z=12};
	lbIncludeDomains={cls="static";text="搜索域名：";left=34;top=268;right=101;bottom=295;align="right";dl=1;dt=1;transparent=1;z=10};
	lnkExa={cls="syslink";text='<a href="https://exa.ai/">exa.ai</a>';left=269;top=25;right=412;bottom=43;bgcolor=0xFFFFFF;dl=1;dt=1;z=16};
	lnkTavily={cls="syslink";text='<a href="https://app.tavily.com/home">tavily.com</a>';left=261;top=117;right=404;bottom=135;bgcolor=0xFFFFFF;db=1;dl=1;z=17}
	)
	
	frmSearch.editResult.text = /*
- 每次清除上下文之前仅联网搜索一次。
大模型的上下文是有限的，注意首次的提示词对搜索友好。

- 已限定使用对 aardio 问题效果最好的 exa.ai 站内搜索，
改用 ImTip 自带的 AI 助手可修改选项或使用其他 AI 接口。

- 使用 aardio 官方 AI 接口自动忽略此功能，
F1 键沉浸式 AI 助手自动忽略此功能，"""选中代码""" 上下文问答自动忽略此功能。
aardio 的 AI 接口已自带更快效果更好 aardio 知识库。

立即开通 aardio 专业版 AI 接口，更快更好更智能！
https://aardio.com/vip/
*/
	
	frmSearch.chkTavily.oncommand = function(id,event){
		if(frmSearch.chkTavily.checked){
			frmSearch.chkExa.checked = false;
		}
	}
	
	frmSearch.chkExa.oncommand = function(id,event){
		if(frmSearch.chkExa.checked){
			frmSearch.chkTavily.checked = false;
		}
	}
	
	if(!config.search){
		config.search = {}
	};
	if(config.search.mode = "exa"){
		frmSearch.chkExa.checked = true;
	}
	elseif(config.search.mode = "tavily"){
		frmSearch.chkTavily.checked = true;
	}
	
	if(!config.search.exa){
		config.search.exa = {};
	}
	
	frmSearch.editSearchCount.text  = config.search.exa.count;
	if(#config.search.exa.includeDomains){
		frmSearch.editExaIncludeDomains.text  = string.join(config.search.exa.includeDomains,",");
	}
	else {
		frmSearch.editExaIncludeDomains.text = "www.aardio.com"
	}
	
	if(#config.search.exa.excludeDomains){
		frmSearch.editExaExcludeDomains.text  = string.join(config.search.exa.excludeDomains,",");
	} 
		
	frmSearch.editExaKey.text = config.search.exa[["key"]]; 
	frmSearch.editTavilyKey.text = config.search.tavily[["key"]];
		
	frmSearch.editExaIncludeDomains.text = "www.aardio.com";
	frmSearch.editExaIncludeDomains.disabled = true;
	frmSearch.editExaExcludeDomains.disabled = true; 
	 
	frmSearch.btnSave.oncommand = function(id,event){
	 
		config.search.exa = {
			key = frmSearch.editExaKey.text;
			count = tonumber(frmSearch.editSearchCount.text);
			includeDomains = #frmSearch.editExaIncludeDomains.text ? string.split(frmSearch.editExaIncludeDomains.text,",") : null;
			excludeDomains = #frmSearch.editExaExcludeDomains.text ? string.split(frmSearch.editExaExcludeDomains.text,",") : null;
		}
		
		if(#config.search.exa.includeDomains){
			if(#config.search.exa.excludeDomains){
				config.search.exa.excludeDomains = null;
				frmSearch.editExaExcludeDomains.showErrorTip("指定包含域名以后，不能再指定排除域名");
				return;
			}
		}
		
		config.search.tavily = {
			key = frmSearch.editTavilyKey.text;
		}
		
		if(frmSearch.chkExa.checked){
			config.search.mode = "exa"
		}
		elseif(frmSearch.chkTavily.checked){
			config.search.mode = "tavily"	
		}
		else {
			config.search.mode = null;
			return;
		}
		
		if(!#frmSearch.editQuery.text){
			frmSearch.editQuery.showInfoTip("已保存设置，但测试搜索的内容为空");
			return ; 
		}
		
		frmSearch.btnSave.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}
		frmSearch.editResult.text = ""
	 
		thread.invoke( function(frmSearch,searchConfig){ 
			import web.rest.jsonClient; 
			
			if(frmSearch.chkExa.checked){
				//导入 Exa 索接口 
				var exaClient = web.rest.jsonClient(); 
				exaClient.setHeaders({ "x-api-key": frmSearch.editExaKey.text} )
				var exa = exaClient.api("https://api.exa.ai/");
				
				//搜索
				var searchData,err = exa.search({
    				query:"aardio 编程语言: " + frmSearch.editQuery.text,
    				contents ={ text = true},
    				includeDomains = searchConfig.exa.includeDomains,
    				numResults: tonumber( frmSearch.editSearchCount.text ),
    				type:"keyword" //一般 keyword 搜索就够了（价格低一些）
				})  
				
				var ret  = searchData[["results"]]
				
				if(!frmSearch.editResult.valid) return;
				if(ret){
					frmSearch.editResult.print(ret);	
				}
				elseif(err){
					frmSearch.editResult.print(err);
				}		
			}
			elseif(frmSearch.chkTavily.checked){
				var http = web.rest.jsonClient(); 
				http.setAuthToken(frmSearch.editTavilyKey.text); 
				var tavily = http.api("https://api.tavily.com");
  
				//搜索
				var searchData,err = tavily.search({ 
    				query = "aardio 编程语言: " + frmSearch.editQuery.text,
    				includeDomains = searchConfig.exa.includeDomains,
    				max_results =  tonumber( frmSearch.editSearchCount.text ); //返回的搜索结果数量，不必要太多，前两三条就可以了
				})
				
				//将搜索结果添加到系统提示词
				var ret = searchData[["results"]]
				
				if(!frmSearch.editResult.valid) return;
				if(ret){
					frmSearch.editResult.print(ret);	
				}
				elseif(err){
					frmSearch.editResult.print(err);
				}	
			} 
			
			frmSearch.btnSave.disabledText = null;
		},frmSearch,config.search)
	}
	
	var chkStyle = {
		color={
			active=0xFF00FF00;
			default=0xFF000000;
			disabled=0xEE666666;
			hover=0xFFFF0000		
		};
		checked={
			iconText='\uF14A'		
		};
	}
	
	frmSearch.btnSave.skin({
		color={
			active=0xFF00FF00;
			default=0xFF3C3C3C;
			disabled=0xFF6D6D6D;
			hover=0xFFFF0000		
		}
		iconColor = {
			disabled=0xFF6D6D6D;
		}
	})
	
	frmSearch.chkTavily.skin(chkStyle); 
	frmSearch.chkExa.skin(chkStyle);
	frmSearch.doModal(winform);
	
	winform.editPrompt.setFocus();
}


if(config.itemNames){
 	winform.cmbAgent.items = config.itemNames;
}

if(config.selItem){
 	winform.cmbAgent.selIndex = config.selItem;
} 

winform.cmbAgent.onOk = function(){ 
 	if(winform.cmbAgent.selIndex){
 		var configItem = config.itemData[winform.cmbAgent.selIndex];
 		if(configItem){
 			aiChatConfig = ..table.assign({},configItem); 
 			
 			if(!wb.started()){
 				if( !( _ASK_AI_SYSTEMP_PROMPT  && string.find(winform.editPrompt.text,`<"""选中代码""">|<"""报错信息""">`) )){
 					resetMessages();
 				} 
 			} 
 			
 		}		
 	}
}

win.loopMessage();
```

