aaa
Some checks failed
Flask 提示词大师 - CI/CD 流水线 / 代码质量检查 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 单元测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 集成测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 构建Docker镜像 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到测试环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到生产环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署监控系统 (push) Has been cancelled
Some checks failed
Flask 提示词大师 - CI/CD 流水线 / 代码质量检查 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 单元测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 集成测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 构建Docker镜像 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到测试环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到生产环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署监控系统 (push) Has been cancelled
This commit is contained in:
@@ -1,17 +1,29 @@
|
|||||||
# 提示词智能优化 Android App
|
# 提示词智能优化(本地模型)Android App
|
||||||
|
|
||||||
调用提示词大师 3 号专家接口,实现智能提示词生成。
|
基于《生成专业提示词代码逻辑分析》,本地调用 LLM 实现智能提示词生成,**不依赖远程 Flask API**。
|
||||||
|
|
||||||
|
## 功能
|
||||||
|
|
||||||
|
- **两阶段专家逻辑**:先做意图分析(技术/创意/分析/咨询),再按领域选择专家模板生成提示词
|
||||||
|
- **本地模型调用**:通过 OpenAI 兼容接口(如 Ollama)本地推理
|
||||||
|
- **配置灵活**:支持设置 API 地址和模型名称
|
||||||
|
|
||||||
## 环境
|
## 环境
|
||||||
|
|
||||||
- Android Studio(推荐使用 AGP 8.7+、Gradle 8.9)
|
- Android Studio(推荐 AGP 8.7+、Gradle 8.9)
|
||||||
- minSdk 24,targetSdk 34
|
- minSdk 24,targetSdk 34
|
||||||
- Java 17
|
- Java 17
|
||||||
|
|
||||||
## 接口
|
## 使用前准备
|
||||||
|
|
||||||
- 地址:`POST http://101.43.95.130:5002/api/open/expert-generate-3`
|
1. **安装 Ollama(或其他 OpenAI 兼容服务)**
|
||||||
- 参数:`input_text`(必填)、`temperature`、`max_tokens`、`timeout`、`uid`
|
- 官网:https://ollama.ai
|
||||||
|
- 运行:`ollama run qwen2:0.5b`(或其它兼容模型)
|
||||||
|
|
||||||
|
2. **配置 API 地址**
|
||||||
|
- 模拟器:`http://10.0.2.2:11434/v1/`(10.0.2.2 指向宿主机 localhost)
|
||||||
|
- 真机:`http://<电脑局域网 IP>:11434/v1/`(如 `http://192.168.1.100:11434/v1/`)
|
||||||
|
- 在 App 内点击「设置」可修改地址和模型名
|
||||||
|
|
||||||
## 构建
|
## 构建
|
||||||
|
|
||||||
@@ -24,6 +36,6 @@ cd exampleAiApp01
|
|||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
- 需配置网络权限,并允许明文 HTTP(101.43.95.130 为 HTTP)
|
- 需配置网络权限,并允许明文 HTTP(`network_security_config.xml` 已配置)
|
||||||
- 确保设备可访问 `101.43.95.130:5002`
|
- 确保设备与 Ollama 所在电脑处于同一局域网(真机)或使用模拟器
|
||||||
- 如缺少启动图标,可在 Android Studio 中通过 `File > New > Image Asset` 生成
|
- 如缺少启动图标,可在 Android Studio 中通过 `File > New > Image Asset` 生成
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.PromptOptimizer"
|
android:theme="@style/Theme.PromptOptimizer"
|
||||||
android:usesCleartextTraffic="true">
|
android:usesCleartextTraffic="true">
|
||||||
|
<activity
|
||||||
|
android:name=".SettingsActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Theme.PromptOptimizer" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.example.promptoptimizer;
|
|||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -13,20 +14,19 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import com.example.promptoptimizer.api.ExpertGenerate3Api;
|
import com.example.promptoptimizer.llm.LocalLlmClient;
|
||||||
import com.example.promptoptimizer.model.ExpertGenerate3Request;
|
import com.example.promptoptimizer.llm.PromptGenerator;
|
||||||
import com.example.promptoptimizer.model.ExpertGenerate3Response;
|
import com.example.promptoptimizer.model.ExpertGenerate3Response;
|
||||||
import com.google.android.material.button.MaterialButton;
|
import com.google.android.material.button.MaterialButton;
|
||||||
import com.google.android.material.card.MaterialCardView;
|
import com.google.android.material.card.MaterialCardView;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
import retrofit2.Call;
|
import java.util.concurrent.Executors;
|
||||||
import retrofit2.Callback;
|
|
||||||
import retrofit2.Response;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提示词智能优化 - 主界面
|
* 提示词智能优化 - 本地模型调用(两阶段专家逻辑)
|
||||||
|
* 参考《生成专业提示词代码逻辑分析》
|
||||||
*/
|
*/
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private EditText etInput;
|
private EditText etInput;
|
||||||
private MaterialButton btnGenerate;
|
private MaterialButton btnGenerate;
|
||||||
|
private MaterialButton btnSettings;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
private TextView tvLoading;
|
private TextView tvLoading;
|
||||||
private MaterialCardView cardResult;
|
private MaterialCardView cardResult;
|
||||||
@@ -42,14 +43,21 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
private MaterialButton btnCopy;
|
private MaterialButton btnCopy;
|
||||||
|
|
||||||
private boolean isSubmitting = false;
|
private boolean isSubmitting = false;
|
||||||
|
private LocalLlmClient llmClient;
|
||||||
|
private PromptGenerator promptGenerator;
|
||||||
|
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
llmClient = new LocalLlmClient(this);
|
||||||
|
promptGenerator = new PromptGenerator(llmClient);
|
||||||
|
|
||||||
etInput = findViewById(R.id.etInput);
|
etInput = findViewById(R.id.etInput);
|
||||||
btnGenerate = findViewById(R.id.btnGenerate);
|
btnGenerate = findViewById(R.id.btnGenerate);
|
||||||
|
btnSettings = findViewById(R.id.btnSettings);
|
||||||
progressBar = findViewById(R.id.progressBar);
|
progressBar = findViewById(R.id.progressBar);
|
||||||
tvLoading = findViewById(R.id.tvLoading);
|
tvLoading = findViewById(R.id.tvLoading);
|
||||||
cardResult = findViewById(R.id.cardResult);
|
cardResult = findViewById(R.id.cardResult);
|
||||||
@@ -59,6 +67,15 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
btnGenerate.setOnClickListener(v -> doGenerate());
|
btnGenerate.setOnClickListener(v -> doGenerate());
|
||||||
btnCopy.setOnClickListener(v -> copyPrompt());
|
btnCopy.setOnClickListener(v -> copyPrompt());
|
||||||
|
if (btnSettings != null) {
|
||||||
|
btnSettings.setOnClickListener(v -> startActivity(new Intent(this, SettingsActivity.class)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
llmClient.resetApi();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doGenerate() {
|
private void doGenerate() {
|
||||||
@@ -76,45 +93,27 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
btnGenerate.setEnabled(false);
|
btnGenerate.setEnabled(false);
|
||||||
cardResult.setVisibility(View.GONE);
|
cardResult.setVisibility(View.GONE);
|
||||||
|
|
||||||
ExpertGenerate3Request request = new ExpertGenerate3Request(input);
|
executor.execute(() -> {
|
||||||
ExpertGenerate3Api api = RetrofitClient.getApi();
|
try {
|
||||||
|
ExpertGenerate3Response result = promptGenerator.generate(input, 0.7f, 1000);
|
||||||
api.generate(request).enqueue(new Callback<ExpertGenerate3Response>() {
|
runOnUiThread(() -> {
|
||||||
@Override
|
|
||||||
public void onResponse(Call<ExpertGenerate3Response> call,
|
|
||||||
Response<ExpertGenerate3Response> response) {
|
|
||||||
isSubmitting = false;
|
isSubmitting = false;
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
tvLoading.setVisibility(View.GONE);
|
tvLoading.setVisibility(View.GONE);
|
||||||
btnGenerate.setEnabled(true);
|
btnGenerate.setEnabled(true);
|
||||||
|
showResult(result);
|
||||||
if (response.isSuccessful() && response.body() != null) {
|
});
|
||||||
ExpertGenerate3Response body = response.body();
|
} catch (Exception e) {
|
||||||
if (body.getCode() == 200 && body.getData() != null) {
|
Log.e(TAG, "本地生成失败", e);
|
||||||
showResult(body);
|
runOnUiThread(() -> {
|
||||||
} else {
|
|
||||||
String msg = body.getMessage() != null ? body.getMessage() : getString(R.string.error_network);
|
|
||||||
Log.w(TAG, "API 业务错误: code=" + body.getCode() + ", message=" + msg);
|
|
||||||
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
String err = "HTTP " + response.code() + (response.errorBody() != null ? ": " + response.message() : "");
|
|
||||||
Log.e(TAG, "API 请求失败: " + err);
|
|
||||||
Toast.makeText(MainActivity.this, getString(R.string.error_network) + " " + err,
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Call<ExpertGenerate3Response> call, Throwable t) {
|
|
||||||
isSubmitting = false;
|
isSubmitting = false;
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
tvLoading.setVisibility(View.GONE);
|
tvLoading.setVisibility(View.GONE);
|
||||||
btnGenerate.setEnabled(true);
|
btnGenerate.setEnabled(true);
|
||||||
String detail = t != null ? t.getMessage() : "unknown";
|
Toast.makeText(MainActivity.this,
|
||||||
Log.e(TAG, "网络请求失败: " + detail, t);
|
getString(R.string.error_network) + ": " + e.getMessage(),
|
||||||
Toast.makeText(MainActivity.this, getString(R.string.error_network) + ": " + detail,
|
|
||||||
Toast.LENGTH_LONG).show();
|
Toast.LENGTH_LONG).show();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -151,4 +150,10 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
Toast.makeText(this, R.string.toast_copied, Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, R.string.toast_copied, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
executor.shutdown();
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.example.promptoptimizer;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.example.promptoptimizer.llm.LlmConfig;
|
||||||
|
import com.google.android.material.textfield.TextInputEditText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地 LLM 配置
|
||||||
|
*/
|
||||||
|
public class SettingsActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private TextInputEditText etBaseUrl;
|
||||||
|
private TextInputEditText etModel;
|
||||||
|
private Button btnSave;
|
||||||
|
private LlmConfig config;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_settings);
|
||||||
|
|
||||||
|
config = new LlmConfig(this);
|
||||||
|
etBaseUrl = findViewById(R.id.etBaseUrl);
|
||||||
|
etModel = findViewById(R.id.etModel);
|
||||||
|
btnSave = findViewById(R.id.btnSave);
|
||||||
|
|
||||||
|
etBaseUrl.setText(config.getBaseUrl());
|
||||||
|
etModel.setText(config.getModel());
|
||||||
|
|
||||||
|
btnSave.setOnClickListener(v -> {
|
||||||
|
String url = etBaseUrl.getText() != null ? etBaseUrl.getText().toString().trim() : "";
|
||||||
|
String model = etModel.getText() != null ? etModel.getText().toString().trim() : "";
|
||||||
|
if (url.isEmpty()) {
|
||||||
|
Toast.makeText(this, "请填写 API 地址", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config.setBaseUrl(url);
|
||||||
|
config.setModel(model.isEmpty() ? "qwen2:0.5b" : model);
|
||||||
|
Toast.makeText(this, "已保存", Toast.LENGTH_SHORT).show();
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.example.promptoptimizer.llm;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenAI 兼容的 Chat Completions 请求
|
||||||
|
*/
|
||||||
|
public class ChatCompletionRequest {
|
||||||
|
|
||||||
|
@SerializedName("model")
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
@SerializedName("messages")
|
||||||
|
private List<Message> messages;
|
||||||
|
|
||||||
|
@SerializedName("temperature")
|
||||||
|
private Float temperature;
|
||||||
|
|
||||||
|
@SerializedName("max_tokens")
|
||||||
|
private Integer maxTokens;
|
||||||
|
|
||||||
|
@SerializedName("stream")
|
||||||
|
private Boolean stream = false;
|
||||||
|
|
||||||
|
public ChatCompletionRequest(String model, List<Message> messages, Float temperature, Integer maxTokens) {
|
||||||
|
this.model = model;
|
||||||
|
this.messages = messages;
|
||||||
|
this.temperature = temperature;
|
||||||
|
this.maxTokens = maxTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Message {
|
||||||
|
@SerializedName("role")
|
||||||
|
private String role;
|
||||||
|
|
||||||
|
@SerializedName("content")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
public Message(String role, String content) {
|
||||||
|
this.role = role;
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRole() { return role; }
|
||||||
|
public String getContent() { return content; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.example.promptoptimizer.llm;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenAI 兼容的 Chat Completions 响应
|
||||||
|
*/
|
||||||
|
public class ChatCompletionResponse {
|
||||||
|
|
||||||
|
@SerializedName("choices")
|
||||||
|
private List<Choice> choices;
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
if (choices != null && !choices.isEmpty()) {
|
||||||
|
Choice c = choices.get(0);
|
||||||
|
if (c != null && c.getMessage() != null) {
|
||||||
|
return c.getMessage().getContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Choice {
|
||||||
|
@SerializedName("message")
|
||||||
|
private Message message;
|
||||||
|
|
||||||
|
public Message getMessage() { return message; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Message {
|
||||||
|
@SerializedName("content")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
public String getContent() { return content; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.example.promptoptimizer.llm;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地 LLM 配置
|
||||||
|
*/
|
||||||
|
public class LlmConfig {
|
||||||
|
|
||||||
|
private static final String PREFS = "llm_config";
|
||||||
|
private static final String KEY_BASE_URL = "base_url";
|
||||||
|
private static final String KEY_MODEL = "model";
|
||||||
|
|
||||||
|
// 默认:Ollama 本地。模拟器用 10.0.2.2 访问宿主机
|
||||||
|
private static final String DEFAULT_BASE_URL = "http://10.0.2.2:11434/v1/";
|
||||||
|
private static final String DEFAULT_MODEL = "qwen2:0.5b";
|
||||||
|
|
||||||
|
private final SharedPreferences prefs;
|
||||||
|
|
||||||
|
public LlmConfig(Context context) {
|
||||||
|
prefs = context.getApplicationContext().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBaseUrl() {
|
||||||
|
return prefs.getString(KEY_BASE_URL, DEFAULT_BASE_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBaseUrl(String url) {
|
||||||
|
if (url != null && !url.endsWith("/")) url = url + "/";
|
||||||
|
prefs.edit().putString(KEY_BASE_URL, url).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModel() {
|
||||||
|
return prefs.getString(KEY_MODEL, DEFAULT_MODEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(String model) {
|
||||||
|
prefs.edit().putString(KEY_MODEL, model != null ? model : DEFAULT_MODEL).apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.example.promptoptimizer.llm;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.Body;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenAI 兼容的 LLM API(Ollama / 本地部署等)
|
||||||
|
*/
|
||||||
|
public interface LocalLlmApi {
|
||||||
|
|
||||||
|
@POST("chat/completions")
|
||||||
|
Call<ChatCompletionResponse> chat(@Body ChatCompletionRequest request);
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.example.promptoptimizer.llm;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor;
|
||||||
|
import retrofit2.Retrofit;
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地 LLM 客户端(支持 Ollama 等 OpenAI 兼容 API)
|
||||||
|
*/
|
||||||
|
public class LocalLlmClient {
|
||||||
|
|
||||||
|
private final LlmConfig config;
|
||||||
|
private LocalLlmApi api;
|
||||||
|
|
||||||
|
public LocalLlmClient(Context context) {
|
||||||
|
config = new LlmConfig(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalLlmApi getApi() {
|
||||||
|
if (api == null) {
|
||||||
|
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
|
||||||
|
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||||
|
|
||||||
|
OkHttpClient client = new OkHttpClient.Builder()
|
||||||
|
.connectTimeout(120, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(120, TimeUnit.SECONDS)
|
||||||
|
.writeTimeout(120, TimeUnit.SECONDS)
|
||||||
|
.addInterceptor(interceptor)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Retrofit retrofit = new Retrofit.Builder()
|
||||||
|
.baseUrl(config.getBaseUrl())
|
||||||
|
.client(client)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.build();
|
||||||
|
api = retrofit.create(LocalLlmApi.class);
|
||||||
|
}
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetApi() {
|
||||||
|
api = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBaseUrl() { return config.getBaseUrl(); }
|
||||||
|
public String getModel() { return config.getModel(); }
|
||||||
|
}
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
package com.example.promptoptimizer.llm;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.example.promptoptimizer.model.ExpertGenerate3Response;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 两阶段专家提示词生成(参考《生成专业提示词代码逻辑分析》)
|
||||||
|
* 第一阶段:意图分析 → 第二阶段:领域专家生成
|
||||||
|
*/
|
||||||
|
public class PromptGenerator {
|
||||||
|
|
||||||
|
private static final String TAG = "PromptGenerator";
|
||||||
|
|
||||||
|
private static final String INTENT_PROMPT = "你是一位资深的意图分析专家,请分析用户输入的意图和需求。\n\n" +
|
||||||
|
"你必须严格按照以下JSON格式返回,不要添加任何其他内容:\n" +
|
||||||
|
"{\n" +
|
||||||
|
" \"core_intent\": \"技术\",\n" +
|
||||||
|
" \"domain\": \"具体专业领域\",\n" +
|
||||||
|
" \"key_requirements\": [\"需求1\", \"需求2\"],\n" +
|
||||||
|
" \"expected_output\": \"期望输出的具体形式\",\n" +
|
||||||
|
" \"constraints\": [\"约束1\", \"约束2\"],\n" +
|
||||||
|
" \"keywords\": [\"关键词1\", \"关键词2\"]\n" +
|
||||||
|
"}\n\n" +
|
||||||
|
"注意:\n" +
|
||||||
|
"1. 严格遵守JSON格式\n" +
|
||||||
|
"2. core_intent必须是以下之一:技术、创意、分析、咨询\n" +
|
||||||
|
"3. 数组至少包含1个元素\n" +
|
||||||
|
"4. 所有字段都必须存在\n" +
|
||||||
|
"5. 不要包含注释\n" +
|
||||||
|
"6. 不要添加任何额外的文本";
|
||||||
|
|
||||||
|
private static final Map<String, String> EXPERT_TEMPLATES = new HashMap<>();
|
||||||
|
static {
|
||||||
|
EXPERT_TEMPLATES.put("技术", "你是一位专业的技术领域提示工程师。基于以下意图分析,生成一个专业的技术任务提示词:\n\n意图分析:\n{analysis}\n\n请生成的提示词包含:\n1. 明确的技术背景和上下文\n2. 具体的技术要求和规范\n3. 性能和质量标准\n4. 技术约束条件\n5. 预期交付成果\n6. 评估标准\n\n使用专业技术术语,确保提示词的可执行性和可验证性。");
|
||||||
|
EXPERT_TEMPLATES.put("创意", "你是一位专业的创意领域提示工程师。基于以下意图分析,生成一个创意设计提示词:\n\n意图分析:\n{analysis}\n\n请生成的提示词包含:\n1. 创意方向和灵感来源\n2. 风格和氛围要求\n3. 目标受众定义\n4. 设计元素规范\n5. 创意表现形式\n6. 评估标准\n\n使用专业创意术语,确保提示词的创新性和可执行性。");
|
||||||
|
EXPERT_TEMPLATES.put("分析", "你是一位专业的数据分析提示工程师。基于以下意图分析,生成一个数据分析提示词:\n\n意图分析:\n{analysis}\n\n请生成的提示词包含:\n1. 分析目标和范围\n2. 数据要求和规范\n3. 分析方法和工具\n4. 输出格式要求\n5. 关键指标定义\n6. 质量控制标准\n\n使用专业分析术语,确保提示词的科学性和可操作性。");
|
||||||
|
EXPERT_TEMPLATES.put("咨询", "你是一位专业的咨询领域提示工程师。基于以下意图分析,生成一个咨询服务提示词:\n\n意图分析:\n{analysis}\n\n请生成的提示词包含:\n1. 咨询问题界定\n2. 背景信息要求\n3. 分析框架设定\n4. 建议输出格式\n5. 实施考虑因素\n6. 效果评估标准\n\n使用专业咨询术语,确保提示词的专业性和实用性。");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String FALLBACK_TEMPLATE = "你是一位专业的通用领域提示工程师。基于以下意图分析,生成一个专业的提示词:\n\n意图分析:\n{analysis}\n\n请生成的提示词包含:\n1. 明确的目标定义\n2. 具体要求和规范\n3. 质量标准\n4. 约束条件\n5. 预期输出\n6. 评估标准\n\n确保提示词的清晰性和可执行性。";
|
||||||
|
|
||||||
|
private final LocalLlmClient llmClient;
|
||||||
|
private final Gson gson = new Gson();
|
||||||
|
|
||||||
|
public PromptGenerator(LocalLlmClient llmClient) {
|
||||||
|
this.llmClient = llmClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步执行两阶段生成(需在后台线程调用)
|
||||||
|
*/
|
||||||
|
public ExpertGenerate3Response generate(String userInput, float temperature, int maxTokens) throws Exception {
|
||||||
|
String model = llmClient.getModel();
|
||||||
|
LocalLlmApi api = llmClient.getApi();
|
||||||
|
|
||||||
|
// 第一阶段:意图分析
|
||||||
|
List<ChatCompletionRequest.Message> intentMessages = Arrays.asList(
|
||||||
|
new ChatCompletionRequest.Message("system", INTENT_PROMPT),
|
||||||
|
new ChatCompletionRequest.Message("user", userInput)
|
||||||
|
);
|
||||||
|
ChatCompletionRequest intentReq = new ChatCompletionRequest(model, intentMessages, 0.1f, 1024);
|
||||||
|
Response<ChatCompletionResponse> intentResp = api.chat(intentReq).execute();
|
||||||
|
if (!intentResp.isSuccessful() || intentResp.body() == null) {
|
||||||
|
throw new Exception("意图分析请求失败: " + (intentResp.errorBody() != null ? intentResp.errorBody().string() : intentResp.code()));
|
||||||
|
}
|
||||||
|
String intentRaw = intentResp.body().getContent();
|
||||||
|
if (intentRaw == null || intentRaw.isEmpty()) {
|
||||||
|
throw new Exception("意图分析返回为空");
|
||||||
|
}
|
||||||
|
intentRaw = intentRaw.replace("```json", "").replace("```", "").trim();
|
||||||
|
|
||||||
|
IntentAnalysis intent = parseIntent(intentRaw);
|
||||||
|
if (intent == null) {
|
||||||
|
throw new Exception("意图分析解析失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二阶段:领域专家生成
|
||||||
|
String template = EXPERT_TEMPLATES.getOrDefault(intent.coreIntent, FALLBACK_TEMPLATE);
|
||||||
|
String analysisStr = gson.toJson(intent.toMap());
|
||||||
|
String expertPrompt = template.replace("{analysis}", analysisStr);
|
||||||
|
|
||||||
|
List<ChatCompletionRequest.Message> expertMessages = Arrays.asList(
|
||||||
|
new ChatCompletionRequest.Message("system", expertPrompt),
|
||||||
|
new ChatCompletionRequest.Message("user", userInput)
|
||||||
|
);
|
||||||
|
ChatCompletionRequest expertReq = new ChatCompletionRequest(model, expertMessages, temperature, maxTokens);
|
||||||
|
Response<ChatCompletionResponse> expertResp = api.chat(expertReq).execute();
|
||||||
|
if (!expertResp.isSuccessful() || expertResp.body() == null) {
|
||||||
|
throw new Exception("提示词生成请求失败: " + expertResp.code());
|
||||||
|
}
|
||||||
|
String generated = expertResp.body().getContent();
|
||||||
|
if (generated == null || generated.isEmpty()) {
|
||||||
|
throw new Exception("生成结果为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpertGenerate3Response result = new ExpertGenerate3Response();
|
||||||
|
result.setCode(200);
|
||||||
|
result.setMessage("success");
|
||||||
|
ExpertGenerate3Response.ResponseData data = new ExpertGenerate3Response.ResponseData();
|
||||||
|
data.setIntentAnalysis(intent.toIntentAnalysis());
|
||||||
|
data.setGeneratedPrompt(generated.trim());
|
||||||
|
result.setData(data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private IntentAnalysis parseIntent(String json) {
|
||||||
|
try {
|
||||||
|
Map<String, Object> map = gson.fromJson(json, Map.class);
|
||||||
|
if (map == null) return null;
|
||||||
|
|
||||||
|
IntentAnalysis ia = new IntentAnalysis();
|
||||||
|
ia.coreIntent = safeStr(map.get("core_intent"), "技术");
|
||||||
|
if (!Arrays.asList("技术", "创意", "分析", "咨询").contains(ia.coreIntent)) {
|
||||||
|
ia.coreIntent = "技术";
|
||||||
|
}
|
||||||
|
ia.domain = safeStr(map.get("domain"), "未指定");
|
||||||
|
ia.expectedOutput = safeStr(map.get("expected_output"), "未指定");
|
||||||
|
ia.keyRequirements = toStrList(map.get("key_requirements"));
|
||||||
|
ia.constraints = toStrList(map.get("constraints"));
|
||||||
|
ia.keywords = toStrList(map.get("keywords"));
|
||||||
|
return ia;
|
||||||
|
} catch (JsonSyntaxException e) {
|
||||||
|
Log.e(TAG, "JSON 解析失败: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String safeStr(Object o, String def) {
|
||||||
|
return o != null ? String.valueOf(o).trim() : def;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static String[] toStrList(Object o) {
|
||||||
|
if (o instanceof List) {
|
||||||
|
List<?> list = (List<?>) o;
|
||||||
|
List<String> out = new ArrayList<>();
|
||||||
|
for (Object item : list) {
|
||||||
|
out.add(String.valueOf(item));
|
||||||
|
}
|
||||||
|
return out.isEmpty() ? new String[]{"未指定"} : out.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
return new String[]{"未指定"};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class IntentAnalysis {
|
||||||
|
String coreIntent;
|
||||||
|
String domain;
|
||||||
|
String expectedOutput;
|
||||||
|
String[] keyRequirements;
|
||||||
|
String[] constraints;
|
||||||
|
String[] keywords;
|
||||||
|
|
||||||
|
Map<String, Object> toMap() {
|
||||||
|
Map<String, Object> m = new HashMap<>();
|
||||||
|
m.put("core_intent", coreIntent);
|
||||||
|
m.put("domain", domain);
|
||||||
|
m.put("expected_output", expectedOutput);
|
||||||
|
m.put("key_requirements", keyRequirements != null ? Arrays.asList(keyRequirements) : new ArrayList<>());
|
||||||
|
m.put("constraints", constraints != null ? Arrays.asList(constraints) : new ArrayList<>());
|
||||||
|
m.put("keywords", keywords != null ? Arrays.asList(keywords) : new ArrayList<>());
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpertGenerate3Response.IntentAnalysis toIntentAnalysis() {
|
||||||
|
ExpertGenerate3Response.IntentAnalysis out = new ExpertGenerate3Response.IntentAnalysis();
|
||||||
|
out.setCoreIntent(coreIntent);
|
||||||
|
out.setDomain(domain);
|
||||||
|
out.setExpectedOutput(expectedOutput);
|
||||||
|
out.setKeyRequirements(keyRequirements != null ? keyRequirements : new String[]{"未指定"});
|
||||||
|
out.setConstraints(constraints != null ? constraints : new String[]{"未指定"});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,14 +12,27 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="16dp">
|
android:padding="16dp">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="提示词智能优化"
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="提示词智能优化(本地)"
|
||||||
android:textSize="22sp"
|
android:textSize="22sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:textColor="#1E3A8A"
|
android:textColor="#1E3A8A" />
|
||||||
android:layout_marginBottom="16dp" />
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnSettings"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="设置" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
65
exampleAiApp01/app/src/main/res/layout/activity_settings.xml
Normal file
65
exampleAiApp01/app/src/main/res/layout/activity_settings.xml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="本地模型配置"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="24dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="API 地址(Ollama 默认 http://10.0.2.2:11434/v1/)"
|
||||||
|
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/etBaseUrl"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textUri" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="模型名称(如 qwen2:0.5b、llama2)"
|
||||||
|
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_marginBottom="24dp">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/etModel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnSave"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="保存" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="说明:\n1. 请先在电脑安装 Ollama,运行 ollama run qwen2:0.5b\n2. 模拟器访问宿主机用 10.0.2.2,真机用电脑局域网 IP\n3. 例如:http://192.168.1.100:11434/v1/"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="#666666"
|
||||||
|
android:lineSpacingExtra="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
BIN
exampleAiApp01/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
exampleAiApp01/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
172
exampleAiApp01/gradlew
vendored
Executable file
172
exampleAiApp01/gradlew
vendored
Executable file
@@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
@@ -10661,3 +10661,4 @@ werkzeug.routing.exceptions.BuildError: Could not build url for endpoint 'expert
|
|||||||
[2026-03-01 23:45:03 +0800] [28566] [INFO] Booting worker with pid: 28566
|
[2026-03-01 23:45:03 +0800] [28566] [INFO] Booting worker with pid: 28566
|
||||||
[2026-03-01 23:45:03 +0800] [28566] [INFO] 工作进程 28566 已启动
|
[2026-03-01 23:45:03 +0800] [28566] [INFO] 工作进程 28566 已启动
|
||||||
[2026-03-01 23:45:03 +0800] [28566] [INFO] 工作进程 28566 初始化完成
|
[2026-03-01 23:45:03 +0800] [28566] [INFO] 工作进程 28566 初始化完成
|
||||||
|
[2026-03-02 00:11:37 +0800] [28391] [INFO] Handling signal: winch
|
||||||
|
|||||||
Reference in New Issue
Block a user