feat: 配置页增加测试连接按钮,发送简单文本验证API是否可用
This commit is contained in:
@@ -26,6 +26,10 @@ pub struct FlomoAiApp {
|
||||
new_prompt_title: String,
|
||||
new_prompt_content: String,
|
||||
|
||||
test_status: String,
|
||||
test_is_loading: bool,
|
||||
pending_test: Option<std::thread::JoinHandle<Result<String, String>>>,
|
||||
|
||||
current_page: Page,
|
||||
theme_dirty: bool,
|
||||
}
|
||||
@@ -54,6 +58,10 @@ impl FlomoAiApp {
|
||||
new_prompt_title: String::new(),
|
||||
new_prompt_content: String::new(),
|
||||
|
||||
test_status: "点击测试连接".to_string(),
|
||||
test_is_loading: false,
|
||||
pending_test: None,
|
||||
|
||||
current_page: Page::Main,
|
||||
theme_dirty: false,
|
||||
}
|
||||
@@ -84,6 +92,28 @@ impl FlomoAiApp {
|
||||
ctx.request_repaint();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(handle) = self.pending_test.take() {
|
||||
if handle.is_finished() {
|
||||
match handle.join() {
|
||||
Ok(Ok(text)) => {
|
||||
self.test_status = format!("连接成功: {}", text.chars().take(30).collect::<String>());
|
||||
self.test_is_loading = false;
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
self.test_status = format!("连接失败: {}", e);
|
||||
self.test_is_loading = false;
|
||||
}
|
||||
Err(_) => {
|
||||
self.test_status = "测试线程错误".to_string();
|
||||
self.test_is_loading = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.pending_test = Some(handle);
|
||||
ctx.request_repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_request(&mut self, ctx: &egui::Context) {
|
||||
@@ -144,6 +174,38 @@ impl FlomoAiApp {
|
||||
self.settings.theme_config.mode = self.settings_selected_theme;
|
||||
let _ = save_settings(&self.settings);
|
||||
}
|
||||
|
||||
fn test_connection(&mut self, ctx: &egui::Context) {
|
||||
if self.test_is_loading {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.settings_base_url.is_empty() {
|
||||
self.test_status = "错误: Base URL 不能为空".to_string();
|
||||
return;
|
||||
}
|
||||
|
||||
self.test_status = "测试中...".to_string();
|
||||
self.test_is_loading = true;
|
||||
|
||||
let settings = self.settings.clone();
|
||||
let ctx_clone = ctx.clone();
|
||||
|
||||
let handle = std::thread::spawn(move || {
|
||||
let mut test_settings = settings.clone();
|
||||
test_settings.llm_config.model = if settings.llm_config.model.is_empty() {
|
||||
"gpt-4o".to_string()
|
||||
} else {
|
||||
settings.llm_config.model.clone()
|
||||
};
|
||||
|
||||
let result = crate::api::call_llm(&test_settings, "你好,请回复OK".to_string(), None);
|
||||
ctx_clone.request_repaint();
|
||||
result
|
||||
});
|
||||
|
||||
self.pending_test = Some(handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for FlomoAiApp {
|
||||
@@ -415,6 +477,31 @@ impl FlomoAiApp {
|
||||
ui.text_edit_singleline(&mut self.settings_model);
|
||||
ui.add_space(12.0);
|
||||
|
||||
// Test connection button
|
||||
ui.horizontal(|ui| {
|
||||
let btn_text = if self.test_is_loading { "测试中..." } else { "测试连接" };
|
||||
let btn = egui::Button::new(btn_text)
|
||||
.fill(egui::Color32::from_rgb(100, 100, 255))
|
||||
.rounding(4.0)
|
||||
.min_size(egui::vec2(80.0, 32.0));
|
||||
|
||||
if ui.add(btn).clicked() && !self.test_is_loading {
|
||||
self.test_connection(ctx);
|
||||
}
|
||||
|
||||
let status_color = if self.test_is_loading {
|
||||
egui::Color32::from_rgb(255, 165, 0)
|
||||
} else if self.test_status.starts_with("连接成功") {
|
||||
egui::Color32::from_rgb(0, 180, 0)
|
||||
} else if self.test_status.starts_with("连接失败") || self.test_status.starts_with("错误") {
|
||||
egui::Color32::RED
|
||||
} else {
|
||||
egui::Color32::GRAY
|
||||
};
|
||||
|
||||
ui.label(egui::RichText::new(&self.test_status).size(11.0).color(status_color));
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.label(egui::RichText::new("主题").size(14.0).strong());
|
||||
|
||||
@@ -50,6 +50,8 @@ class SecondActivity : AppCompatActivity() {
|
||||
private lateinit var etApiKey: EditText
|
||||
private lateinit var btnToggleApiKey: ImageButton
|
||||
private lateinit var etModel: EditText
|
||||
private lateinit var btnTestConnection: Button
|
||||
private lateinit var tvTestStatus: TextView
|
||||
private lateinit var llHeadersList: LinearLayout
|
||||
private lateinit var btnAddHeader: Button
|
||||
private lateinit var layoutHeaderContent: LinearLayout
|
||||
@@ -151,6 +153,8 @@ class SecondActivity : AppCompatActivity() {
|
||||
etApiKey = findViewById(R.id.etApiKey)
|
||||
btnToggleApiKey = findViewById(R.id.btnToggleApiKey)
|
||||
etModel = findViewById(R.id.etModel)
|
||||
btnTestConnection = findViewById(R.id.btnTestConnection)
|
||||
tvTestStatus = findViewById(R.id.tvTestStatus)
|
||||
|
||||
// Header Section
|
||||
llHeadersList = findViewById(R.id.llHeadersList)
|
||||
@@ -235,6 +239,11 @@ class SecondActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
// Test connection button
|
||||
btnTestConnection.setOnClickListener {
|
||||
testConnection()
|
||||
}
|
||||
|
||||
Log.d("SecondActivity", "initViews: Completed")
|
||||
} catch (e: Exception) {
|
||||
Log.e("SecondActivity", "initViews: Error finding views", e)
|
||||
@@ -378,6 +387,76 @@ class SecondActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun testConnection() {
|
||||
val baseUrl = etBaseUrl.text.toString().trim()
|
||||
val apiKey = etApiKey.text.toString().trim()
|
||||
val model = etModel.text.toString().trim()
|
||||
|
||||
if (baseUrl.isEmpty()) {
|
||||
tvTestStatus.text = "错误: Base URL 不能为空"
|
||||
tvTestStatus.setTextColor(ContextCompat.getColor(this, R.color.error))
|
||||
return
|
||||
}
|
||||
|
||||
tvTestStatus.text = "测试中..."
|
||||
tvTestStatus.setTextColor(ContextCompat.getColor(this, R.color.warning))
|
||||
btnTestConnection.isEnabled = false
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val messagesJson = org.json.JSONArray().apply {
|
||||
put(org.json.JSONObject().apply {
|
||||
put("role", "user")
|
||||
put("content", "你好,请回复OK")
|
||||
})
|
||||
}
|
||||
|
||||
val requestBody = org.json.JSONObject().apply {
|
||||
put("model", model)
|
||||
put("messages", messagesJson)
|
||||
put("max_tokens", 10)
|
||||
}
|
||||
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url("$baseUrl/chat/completions")
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.addHeader("Authorization", "Bearer $apiKey")
|
||||
.post(requestBody.toString().toRequestBody("application/json".toMediaType()))
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
btnTestConnection.isEnabled = true
|
||||
if (response.isSuccessful) {
|
||||
val responseBody = response.body?.string()
|
||||
val responseJson = org.json.JSONObject(responseBody ?: "")
|
||||
val choices = responseJson.optJSONArray("choices")
|
||||
if (choices != null && choices.length() > 0) {
|
||||
val message = choices.getJSONObject(0).optJSONObject("message")
|
||||
val content = message?.optString("content", "") ?: ""
|
||||
tvTestStatus.text = "连接成功: ${content.take(20)}"
|
||||
tvTestStatus.setTextColor(ContextCompat.getColor(this@SecondActivity, R.color.success))
|
||||
} else {
|
||||
tvTestStatus.text = "连接成功,但返回格式异常"
|
||||
tvTestStatus.setTextColor(ContextCompat.getColor(this@SecondActivity, R.color.warning))
|
||||
}
|
||||
} else {
|
||||
tvTestStatus.text = "连接失败: ${response.code} ${response.message}"
|
||||
tvTestStatus.setTextColor(ContextCompat.getColor(this@SecondActivity, R.color.error))
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
btnTestConnection.isEnabled = true
|
||||
tvTestStatus.text = "连接失败: ${e.message}"
|
||||
tvTestStatus.setTextColor(ContextCompat.getColor(this@SecondActivity, R.color.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addHeaderEntry(key: String = "", value: String = "") {
|
||||
val view = layoutInflater.inflate(R.layout.header_entry, null)
|
||||
val etKey = view.findViewById<EditText>(R.id.etHeaderKey)
|
||||
|
||||
@@ -229,6 +229,37 @@
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 测试连接按钮和状态 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnTestConnection"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_weight="1"
|
||||
android:text="测试连接"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/white"
|
||||
android:background="@drawable/button_primary_bg"
|
||||
android:minWidth="0dp"
|
||||
android:minHeight="0dp"
|
||||
android:layout_marginEnd="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTestStatus"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="2"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/text_hint"
|
||||
android:text="点击测试连接"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user