Skip to content

Commit ee4756c

Browse files
committed
「优化」
完善md解析 允许收起深度思考内容 自动保存助手参数设置
1 parent ab745df commit ee4756c

5 files changed

Lines changed: 140 additions & 52 deletions

File tree

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ android {
1212
applicationId = "com.xjyzs.aiapi"
1313
minSdk = 24
1414
targetSdk = 36
15-
versionCode = 4
16-
versionName = "1.0.3"
15+
versionCode = 5
16+
versionName = "1.1.0"
1717

1818
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
1919
androidResources. localeFilters+= listOf("zh")

app/src/main/java/com/xjyzs/aiapi/MainActivity.kt

Lines changed: 95 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import androidx.compose.foundation.rememberScrollState
3737
import androidx.compose.foundation.shape.CircleShape
3838
import androidx.compose.foundation.shape.RoundedCornerShape
3939
import androidx.compose.foundation.text.BasicTextField
40+
import androidx.compose.foundation.text.KeyboardOptions
4041
import androidx.compose.foundation.text.selection.SelectionContainer
4142
import androidx.compose.foundation.verticalScroll
4243
import androidx.compose.material.icons.Icons
@@ -45,6 +46,7 @@ import androidx.compose.material.icons.filled.AddCircle
4546
import androidx.compose.material.icons.filled.Api
4647
import androidx.compose.material.icons.filled.ArrowUpward
4748
import androidx.compose.material.icons.filled.Edit
49+
import androidx.compose.material.icons.filled.ExpandMore
4850
import androidx.compose.material.icons.filled.MoreVert
4951
import androidx.compose.material.icons.filled.Refresh
5052
import androidx.compose.material.icons.filled.Settings
@@ -82,6 +84,7 @@ import androidx.compose.runtime.setValue
8284
import androidx.compose.ui.Alignment
8385
import androidx.compose.ui.Modifier
8486
import androidx.compose.ui.draw.clip
87+
import androidx.compose.ui.draw.rotate
8588
import androidx.compose.ui.focus.FocusRequester
8689
import androidx.compose.ui.focus.focusRequester
8790
import androidx.compose.ui.graphics.Color
@@ -95,7 +98,9 @@ import androidx.compose.ui.res.vectorResource
9598
import androidx.compose.ui.text.TextRange
9699
import androidx.compose.ui.text.TextStyle
97100
import androidx.compose.ui.text.font.FontWeight
101+
import androidx.compose.ui.text.input.KeyboardType
98102
import androidx.compose.ui.text.input.TextFieldValue
103+
import androidx.compose.ui.unit.DpOffset
99104
import androidx.compose.ui.unit.dp
100105
import androidx.compose.ui.unit.sp
101106
import androidx.core.content.edit
@@ -124,7 +129,6 @@ import kotlin.math.round
124129

125130
@Keep
126131
data class Message(val role: String, val content: String)
127-
@SuppressLint("MutableCollectionMutableState")
128132
class ChatViewModel : ViewModel() {
129133
var msgs = mutableStateListOf<Message>()
130134
var sessions = mutableStateListOf<String>()
@@ -152,26 +156,28 @@ class ChatViewModel : ViewModel() {
152156

153157

154158
fun updateAIMessage(content: String) {
159+
val newContent=content.replaceFirst(Regex("^\n{0,2}"), "")
155160
viewModelScope.launch(Dispatchers.Main) {
156161
if (msgs.isEmpty() || msgs.last().role != "assistant") {
157-
msgs.add(Message("assistant", content))
162+
msgs.add(Message("assistant", newContent))
158163
} else {
159164
val lastMsg = msgs.last()
160-
msgs[msgs.lastIndex] = lastMsg.copy(content = lastMsg.content + content)
165+
msgs[msgs.lastIndex] = lastMsg.copy(content = lastMsg.content + newContent)
161166
}
162167
}
163168
}
164169

165170
fun updateAIReasoningMessage(content: String) {
171+
val newContent=content.replaceFirst(Regex("^\n{0,2}"), "")
166172
viewModelScope.launch(Dispatchers.Main) {
167173
if (msgs.last().role == "assistant" && msgs.last().content == "") {
168174
msgs.removeAt(msgs.size - 1)
169175
}
170176
if (msgs.isEmpty() || msgs.last().role != "assistant_reasoning") {
171-
msgs.add(Message("assistant_reasoning", content))
177+
msgs.add(Message("assistant_reasoning", newContent))
172178
} else {
173179
val lastMsg = msgs.last()
174-
msgs[msgs.lastIndex] = lastMsg.copy(content = lastMsg.content + content)
180+
msgs[msgs.lastIndex] = lastMsg.copy(content = lastMsg.content + newContent)
175181
}
176182
}
177183
}
@@ -194,7 +200,7 @@ class ChatViewModel : ViewModel() {
194200

195201
fun maxTokensIsNumber(): Boolean {
196202
try {
197-
maxTokens.toInt()
203+
maxTokens.toLong()
198204
return true
199205
} catch (_: Exception) {
200206
return false
@@ -232,6 +238,8 @@ fun MainUI(viewModel: ChatViewModel) {
232238
var currentConfig = currentConfigPref.getString("currentConfig", "")
233239
val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
234240
val scope = rememberCoroutineScope()
241+
val assistantThinkingClosed = remember {mutableStateListOf<Int>()}
242+
val settingsPref = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
235243

236244
fun save(){
237245
if (viewModel.msgs.size > if (systemPrompt.isNotEmpty()){1}else{0}) {
@@ -245,23 +253,25 @@ fun MainUI(viewModel: ChatViewModel) {
245253
}
246254

247255
LaunchedEffect(Unit) {
248-
viewModel.sessions.clear()
256+
viewModel.temperature=settingsPref.getInt("temperature",-1)
257+
viewModel.maxTokens=settingsPref.getString("maxTokens","")!!
258+
viewModel.parseMd=settingsPref.getBoolean("parseMd",true)
249259
viewModel.sessions.addAll(Gson().fromJson(sessionsPref.getString("sessions", "[]")!!,
250260
object : TypeToken<List<String>>() {}.type))
251-
val settingsPref = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
261+
val assistantsPref = context.getSharedPreferences("assistants", Context.MODE_PRIVATE)
252262
val configsList = mutableListOf<String>()
253263
currentConfig = currentConfigPref.getString("currentConfig", "")!!
254-
for (i in settingsPref.all) {
264+
for (i in assistantsPref.all) {
255265
configsList.add(i.key)
256266
}
257-
val settings = JsonParser.parseString(
258-
settingsPref.getString(
267+
val assistants = JsonParser.parseString(
268+
assistantsPref.getString(
259269
currentConfig,
260270
"{'apiUrl':'','apiKey':'','model':'','systemPrompt':''}"
261271
)
262272
).asJsonObject
263-
api_url = settings.get("apiUrl").asString;api_key = settings.get("apiKey").asString;model =
264-
settings.get("model").asString;systemPrompt = settings.get("systemPrompt").asString
273+
api_url = assistants.get("apiUrl").asString;api_key = assistants.get("apiKey").asString;model =
274+
assistants.get("model").asString;systemPrompt = assistants.get("systemPrompt").asString
265275
if (viewModel.sessions.isEmpty()) {
266276
viewModel.sessions.add("新对话" + System.currentTimeMillis().toString())
267277
}
@@ -326,7 +336,7 @@ fun MainUI(viewModel: ChatViewModel) {
326336
currentConfig
327337
} else {
328338
"请先配置 AI API"
329-
}, vibrator
339+
}, vibrator,settingsPref
330340
)
331341
},
332342
bottomBar = {
@@ -401,7 +411,7 @@ fun MainUI(viewModel: ChatViewModel) {
401411
} else {
402412
ImageVector.vectorResource(R.drawable.ic_rectangle)
403413
},
404-
viewModel, vibrator
414+
viewModel, vibrator,settingsPref
405415
)
406416
}
407417
) { innerPadding ->
@@ -470,11 +480,34 @@ fun MainUI(viewModel: ChatViewModel) {
470480
}
471481
}
472482

473-
"assistant_reasoning" -> Row {
474-
Icon(
475-
Icons.Default.Api,
476-
""
477-
);Text(msg.content, color = Color.Gray)
483+
"assistant_reasoning" -> {
484+
var expanded = false
485+
Row(verticalAlignment = Alignment.CenterVertically) {
486+
Icon(
487+
ImageVector.vectorResource(R.drawable.ic_deep_think),
488+
null
489+
)
490+
if (i in assistantThinkingClosed) {
491+
IconButton({ assistantThinkingClosed.remove(i) }) {
492+
Icon(Icons.Default.ExpandMore, null)
493+
}
494+
} else {
495+
expanded = true
496+
IconButton({ assistantThinkingClosed.add(i) }) {
497+
Icon(
498+
Icons.Default.ExpandMore,
499+
null,
500+
Modifier.rotate(180f)
501+
)
502+
}
503+
}
504+
}
505+
if (expanded) {
506+
Row {
507+
Spacer(Modifier.size(22.dp))
508+
Text(msg.content, color = Color.Gray)
509+
}
510+
}
478511
}
479512

480513
"user" -> {
@@ -522,8 +555,8 @@ fun MainUI(viewModel: ChatViewModel) {
522555
else -> Row {
523556
Icon(
524557
Icons.Default.Settings,
525-
""
526-
);Text(msg.content + "\n")
558+
null
559+
);Text(msg.content + "\n",color= MaterialTheme.colorScheme.primary)
527560
}
528561
}
529562
}
@@ -541,7 +574,8 @@ fun MessageInputBar(
541574
onSend: (String) -> Unit,
542575
sendImg: ImageVector,
543576
viewModel: ChatViewModel,
544-
vibrator: Vibrator
577+
vibrator: Vibrator,
578+
settingsPref: SharedPreferences
545579
) {
546580
var temperatureExpanded by remember { mutableStateOf(false) }
547581
var maxTokensExpanded by remember { mutableStateOf(false) }
@@ -641,19 +675,17 @@ fun MessageInputBar(
641675
)
642676
}
643677
}
644-
if (maxTokensExpanded) {
645-
OutlinedTextField(value = viewModel.maxTokens, onValueChange = {
646-
try {
647-
viewModel.maxTokens = it
648-
} catch (_: Exception) {
649-
}
650-
})
651-
}
652678
}
653679
DropdownMenu( // 温度
654680
expanded = temperatureExpanded,
655-
onDismissRequest = { temperatureExpanded = false },
656-
Modifier.width(200.dp)
681+
onDismissRequest = {
682+
temperatureExpanded = false
683+
settingsPref.edit {
684+
putInt("temperature",viewModel.temperature)
685+
}
686+
},
687+
Modifier.width(200.dp),
688+
offset = DpOffset(10.dp,0.dp)
657689
) {
658690
Slider(value = viewModel.temperature.toFloat(), onValueChange = {
659691
val tmp = round(it).toInt()
@@ -663,6 +695,29 @@ fun MessageInputBar(
663695
}
664696
}, valueRange = -1f..20f, steps = 20)
665697
}
698+
DropdownMenu( // 最大 token 数
699+
expanded = maxTokensExpanded,
700+
onDismissRequest = {
701+
maxTokensExpanded = false
702+
settingsPref.edit {
703+
putString("maxTokens",viewModel.maxTokens)
704+
}
705+
},
706+
Modifier.width(150.dp),
707+
offset = DpOffset(100.dp,0.dp)
708+
) {
709+
OutlinedTextField(
710+
value = viewModel.maxTokens, onValueChange = {
711+
try {
712+
viewModel.maxTokens = it
713+
} catch (_: Exception) {
714+
}
715+
},
716+
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
717+
isError = !(viewModel.maxTokensIsNumber() || viewModel.maxTokens.isEmpty()),
718+
singleLine = true
719+
)
720+
}
666721
}
667722
}
668723

@@ -966,7 +1021,7 @@ private fun HistoryDrawer(viewModel: ChatViewModel, context: Context, sessionsPr
9661021

9671022
@OptIn(ExperimentalMaterial3Api::class)
9681023
@Composable
969-
private fun TopBar(viewModel: ChatViewModel,context: Context,drawerState: DrawerState,currentConfig: String,vibrator: Vibrator) {
1024+
private fun TopBar(viewModel: ChatViewModel,context: Context,drawerState: DrawerState,currentConfig: String,vibrator: Vibrator,settingsPref: SharedPreferences) {
9701025
val scope = rememberCoroutineScope()
9711026
var showMenu by remember { mutableStateOf(false) }
9721027
TopAppBar(
@@ -1023,12 +1078,18 @@ private fun TopBar(viewModel: ChatViewModel,context: Context,drawerState: Drawer
10231078
Checkbox(viewModel.parseMd, onCheckedChange = {
10241079
clickVibrate(vibrator)
10251080
viewModel.parseMd = !viewModel.parseMd
1081+
settingsPref.edit {
1082+
putBoolean("parseMd",viewModel.parseMd)
1083+
}
10261084
})
10271085
}
10281086
},
10291087
onClick = {
10301088
clickVibrate(vibrator)
10311089
viewModel.parseMd = !viewModel.parseMd
1090+
settingsPref.edit {
1091+
putBoolean("parseMd",viewModel.parseMd)
1092+
}
10321093
})
10331094
}
10341095
}

app/src/main/java/com/xjyzs/aiapi/SettingsActivity.kt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ class SettingsActivity : ComponentActivity() {
102102
@Composable
103103
fun SettingsUI(viewModel: SettingsViewModel) {
104104
val context = LocalContext.current
105-
val settingsPref = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
105+
val assistantsPref = context.getSharedPreferences("assistants", Context.MODE_PRIVATE)
106106
val currentConfigPref = context.getSharedPreferences("currentConfigPref", Context.MODE_PRIVATE)
107107
val scrollState = rememberScrollState()
108108
var modelsExpanded by remember { mutableStateOf(false) }
@@ -154,17 +154,17 @@ fun SettingsUI(viewModel: SettingsViewModel) {
154154
}
155155
LaunchedEffect(Unit) {
156156
currentConfig = currentConfigPref.getString("currentConfig", "")!!
157-
for (i in settingsPref.all) {
157+
for (i in assistantsPref.all) {
158158
configsList.add(i.key)
159159
}
160-
val settings = JsonParser.parseString(
161-
settingsPref.getString(
160+
val assistants = JsonParser.parseString(
161+
assistantsPref.getString(
162162
currentConfig,
163163
"{'apiUrl':'','apiKey':'','model':'','systemPrompt':''}"
164164
)
165165
).asJsonObject
166-
apiUrl = settings.get("apiUrl").asString;apiKey = settings.get("apiKey").asString;model =
167-
settings.get("model").asString;systemPrompt = settings.get("systemPrompt").asString
166+
apiUrl = assistants.get("apiUrl").asString;apiKey = assistants.get("apiKey").asString;model =
167+
assistants.get("model").asString;systemPrompt = assistants.get("systemPrompt").asString
168168
}
169169
Scaffold(
170170
topBar = {
@@ -223,16 +223,16 @@ fun SettingsUI(viewModel: SettingsViewModel) {
223223
onClick = {
224224
currentConfig = i
225225
configsExpanded = false
226-
val settings = JsonParser.parseString(
227-
settingsPref.getString(
226+
val assistants = JsonParser.parseString(
227+
assistantsPref.getString(
228228
currentConfig,
229229
"{'apiUrl':'','apiKey':'','model':'','systemPrompt':''}"
230230
)
231231
).asJsonObject
232-
apiUrl = settings.get("apiUrl").asString;apiKey =
233-
settings.get("apiKey").asString;model =
234-
settings.get("model").asString;systemPrompt =
235-
settings.get("systemPrompt").asString
232+
apiUrl = assistants.get("apiUrl").asString;apiKey =
233+
assistants.get("apiKey").asString;model =
234+
assistants.get("model").asString;systemPrompt =
235+
assistants.get("systemPrompt").asString
236236
}
237237
)
238238
}
@@ -305,7 +305,7 @@ fun SettingsUI(viewModel: SettingsViewModel) {
305305
)
306306
Button({
307307
if (currentConfig.isNotEmpty()) {
308-
settingsPref.edit {
308+
assistantsPref.edit {
309309
putString(
310310
currentConfig,
311311
Gson().toJson(
@@ -340,7 +340,7 @@ fun SettingsUI(viewModel: SettingsViewModel) {
340340
TextButton(
341341
onClick = {
342342
openDelDialog = false
343-
settingsPref.edit {
343+
assistantsPref.edit {
344344
remove(deletingConfig)
345345
}
346346
configsList.remove(deletingConfig)

app/src/main/java/com/xjyzs/aiapi/utils/Main.kt renamed to app/src/main/java/com/xjyzs/aiapi/utils/MainUtil.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class MarkdownParser {
5959

6060
// 行内样式
6161
private val boldRegex = """\*\*(.*?)\*\*""".toRegex()
62-
private val italicRegex = """\*(.*?)\*""".toRegex()
62+
private val italicRegex = """(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)""".toRegex()
6363
private val codeRegex = """`(.*?)`""".toRegex()
6464
private val deleteRegex = """~~(.*?)~~""".toRegex()
6565

@@ -243,7 +243,8 @@ class MarkdownParser {
243243
@Composable
244244
private fun parseInlineStyles(text: String): AnnotatedString {
245245
val annotatedString = buildAnnotatedString {
246-
append(text)
246+
val cleanText = text.replace("*", "\u200B").replace("`","\u200B").replace("~","\u200B")
247+
append(cleanText)
247248

248249
// 处理粗体
249250
boldRegex.findAll(text).forEach { result ->

0 commit comments

Comments
 (0)