智能体列表,全部

This commit is contained in:
weber
2025-08-06 15:58:17 +08:00
parent 993604bfc1
commit a944bd0fa3
9 changed files with 318 additions and 18 deletions

View File

@@ -1,5 +1,6 @@
package com.aiosman.ravenow.data package com.aiosman.ravenow.data
import com.aiosman.ravenow.AppStore
import com.aiosman.ravenow.data.api.ApiClient import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.entity.AgentEntity import com.aiosman.ravenow.entity.AgentEntity
import com.aiosman.ravenow.entity.ProfileEntity import com.aiosman.ravenow.entity.ProfileEntity
@@ -22,8 +23,6 @@ data class Agent(
val isPublic: Boolean, val isPublic: Boolean,
@SerializedName("openId") @SerializedName("openId")
val openId: String, val openId: String,
@SerializedName("profile")
val profile: Profile,
@SerializedName("title") @SerializedName("title")
val title: String, val title: String,
@SerializedName("updatedAt") @SerializedName("updatedAt")
@@ -38,13 +37,12 @@ data class Agent(
desc = desc, desc = desc,
createdAt = createdAt, createdAt = createdAt,
updatedAt = updatedAt, updatedAt = updatedAt,
avatar = "${ApiClient.BASE_SERVER}$avatar", avatar = "${ApiClient.BASE_API_URL+"/outside"}$avatar"+"?token="+"Bearer ${AppStore.token}",
author = author, //author = author,
isPublic = isPublic, isPublic = isPublic,
openId = openId, openId = openId,
breakMode = breakMode, breakMode = breakMode,
useCount = useCount, useCount = useCount,
profile = profile.toProfileEntity(),
) )
} }
} }

View File

@@ -38,6 +38,11 @@ data class AgentMomentRequestBody(
val sessionId: String val sessionId: String
) )
data class SingleChatRequestBody(
@SerializedName("agentOpenId")
val generateText: String,
)
data class LoginUserRequestBody( data class LoginUserRequestBody(
@SerializedName("username") @SerializedName("username")
val username: String? = null, val username: String? = null,
@@ -470,12 +475,14 @@ interface RaveNowAPI {
suspend fun getAgent( suspend fun getAgent(
@Query("page") page: Int = 1, @Query("page") page: Int = 1,
@Query("pageSize") pageSize: Int = 20, @Query("pageSize") pageSize: Int = 20,
): Response<ListContainer<Agent>> @Query("withWorkflow") withWorkflow: Int = 1,
): Response<DataContainer<ListContainer<Agent>>>
@GET("outside/my/prompts") @GET("outside/my/prompts")
suspend fun getMyAgent( suspend fun getMyAgent(
@Query("page") page: Int = 1, @Query("page") page: Int = 1,
@Query("pageSize") pageSize: Int = 20, @Query("pageSize") pageSize: Int = 20,
@Query("withWorkflow") withWorkflow: Int = 1,
): Response<ListContainer<Agent>> ): Response<ListContainer<Agent>>
@Multipart @Multipart
@@ -483,12 +490,25 @@ interface RaveNowAPI {
suspend fun createAgent( suspend fun createAgent(
@Part avatar: MultipartBody.Part?, @Part avatar: MultipartBody.Part?,
@Part("title") title: RequestBody?, @Part("title") title: RequestBody?,
@Part("value") value: RequestBody?,
@Part("desc") desc: RequestBody?, @Part("desc") desc: RequestBody?,
@Part("workflowId") workflowId: RequestBody?,
@Part("public") isPublic: RequestBody?,
@Part("breakMode") breakMode: RequestBody?,
@Part("useWorkflow") useWorkflow: RequestBody?,
@Part("workflowInputs") workflowInputs: RequestBody?,
): Response<DataContainer<Agent>> ): Response<DataContainer<Agent>>
@POST("generate/postText") @POST("generate/postText")
suspend fun agentMoment(@Body body: AgentMomentRequestBody): Response<DataContainer<String>> suspend fun agentMoment(@Body body: AgentMomentRequestBody): Response<DataContainer<String>>
@POST("outside/rooms/create-single-chat")
suspend fun createSingleChat(@Body body: SingleChatRequestBody): Response<DataContainer<Unit>>
} }

View File

@@ -7,35 +7,48 @@ import com.aiosman.ravenow.data.AgentService
import com.aiosman.ravenow.data.ServiceException import com.aiosman.ravenow.data.ServiceException
import com.aiosman.ravenow.data.UploadImage import com.aiosman.ravenow.data.UploadImage
import com.aiosman.ravenow.data.api.ApiClient import com.aiosman.ravenow.data.api.ApiClient
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import retrofit2.http.Part
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
/** /**
* 智能体 * 智能体
*/ */
suspend fun createAgent( suspend fun createAgent(
title: String, title: String,
desc: String, desc: String,
avatar: UploadImage? = null, avatar: UploadImage? = null,
workflowId:Int = 1,
isPublic:Boolean = true,
breakMode:Boolean = false,
useWorkflow:Boolean = true,
): AgentEntity { ): AgentEntity {
val textTitle = title.toRequestBody("text/plain".toMediaTypeOrNull()) val textTitle = title.toRequestBody("text/plain".toMediaTypeOrNull())
val textDesc = desc.toRequestBody("text/plain".toMediaTypeOrNull()) val textDesc = desc.toRequestBody("text/plain".toMediaTypeOrNull())
val workflowIdRequestBody = workflowId.toString().toRequestBody("text/plain".toMediaTypeOrNull())
val isPublicRequestBody = isPublic.toString().toRequestBody("text/plain".toMediaTypeOrNull())
val breakModeRequestBody = breakMode.toString().toRequestBody("text/plain".toMediaTypeOrNull())
val useWorkflowRequestBody = useWorkflow.toString().toRequestBody("text/plain".toMediaTypeOrNull())
val workflowInputsValue = "{\"si\":\"$desc\"}"
val workflowInputsRequestBody = workflowInputsValue.toRequestBody("text/plain".toMediaTypeOrNull())
val avatarField: MultipartBody.Part? = avatar?.let { val avatarField: MultipartBody.Part? = avatar?.let {
createMultipartBody(it.file, it.filename, "avatar") createMultipartBody(it.file, it.filename, "avatar")
} }
val response = ApiClient.api.createAgent(avatarField, textTitle ,textDesc) val response = ApiClient.api.createAgent(avatarField, textTitle ,textDesc,textDesc,workflowIdRequestBody,isPublicRequestBody,breakModeRequestBody,useWorkflowRequestBody,workflowInputsRequestBody)
val body = response.body()?.data ?: throw ServiceException("Failed to create agent") val body = response.body()?.data ?: throw ServiceException("Failed to create agent")
return body.toAgentEntity() return body.toAgentEntity()
} }
data class AgentEntity( data class AgentEntity(
val author: String, //val author: String,
val avatar: String, val avatar: String,
val breakMode: Boolean, val breakMode: Boolean,
val createdAt: String, val createdAt: String,
@@ -43,7 +56,7 @@ data class AgentEntity(
val id: Int, val id: Int,
val isPublic: Boolean, val isPublic: Boolean,
val openId: String, val openId: String,
val profile: ProfileEntity, //val profile: ProfileEntity,
val title: String, val title: String,
val updatedAt: String, val updatedAt: String,
val useCount: Int val useCount: Int
@@ -80,8 +93,8 @@ class AgentLoader : DataLoader<AgentEntity,AgentLoaderExtraArgs>() {
) )
val data = result.body()?.let { val data = result.body()?.let {
ListContainer( ListContainer(
list = it.list.map { it.toAgentEntity()}, list = it.data.list.map { it.toAgentEntity()},
total = it.total, total = it.data.total,
page = page, page = page,
pageSize = pageSize pageSize = pageSize
) )

View File

@@ -230,6 +230,8 @@ fun AddAgentScreen() {
if (result != null) { if (result != null) {
println("AddAgent: Agent created successfully, closing page") println("AddAgent: Agent created successfully, closing page")
// 创建成功,关闭页面 // 创建成功,关闭页面
model.name = ""
model.desc = ""
navController.popBackStack() navController.popBackStack()
} }
} catch (e: Exception) { } catch (e: Exception) {

View File

@@ -80,7 +80,7 @@ fun AgentCard(
text = agentEntity.title, text = agentEntity.title,
color = AppColors.text, color = AppColors.text,
fontSize = 16.sp, fontSize = 16.sp,
style = TextStyle(fontWeight = FontWeight.Bold) style = TextStyle(fontWeight = FontWeight.W700)
) )
} }
Row( Row(
@@ -93,6 +93,7 @@ fun AgentCard(
modifier = Modifier, modifier = Modifier,
text = agentEntity.desc, text = agentEntity.desc,
color = AppColors.text, color = AppColors.text,
maxLines = 1,
fontSize = 12.sp fontSize = 12.sp
) )
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))

View File

@@ -37,6 +37,7 @@ import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.R import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.NavigationRoute import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine.MineAgent import com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine.MineAgent
import com.aiosman.ravenow.ui.index.tabs.ai.tabs.hot.HotAgent
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -134,7 +135,7 @@ fun Agent() {
Text( Text(
text = stringResource(R.string.agent_mine), text = stringResource(R.string.agent_mine),
fontSize = 14.sp, fontSize = 14.sp,
color = if (pagerState.currentPage == 0) AppColors.checkedText else AppColors.text, color = if (pagerState.currentPage == 0) AppColors.mainText else AppColors.checkedBackground,
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 0) AppColors.checkedBackground else AppColors.unCheckedBackground) .background(if (pagerState.currentPage == 0) AppColors.checkedBackground else AppColors.unCheckedBackground)
@@ -158,7 +159,7 @@ fun Agent() {
Text( Text(
text = stringResource(R.string.agent_hot), text = stringResource(R.string.agent_hot),
fontSize = 14.sp, fontSize = 14.sp,
color = if (pagerState.currentPage == 1) AppColors.checkedText else AppColors.text, color = if (pagerState.currentPage == 1) AppColors.mainText else AppColors.checkedBackground,
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 1) AppColors.checkedBackground else AppColors.unCheckedBackground) .background(if (pagerState.currentPage == 1) AppColors.checkedBackground else AppColors.unCheckedBackground)
@@ -182,7 +183,7 @@ fun Agent() {
Text( Text(
text = stringResource(R.string.agent_recommend), text = stringResource(R.string.agent_recommend),
fontSize = 14.sp, fontSize = 14.sp,
color = if (pagerState.currentPage == 2) AppColors.checkedText else AppColors.text, color = if (pagerState.currentPage == 2) AppColors.mainText else AppColors.checkedBackground,
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 2) AppColors.checkedBackground else AppColors.unCheckedBackground) .background(if (pagerState.currentPage == 2) AppColors.checkedBackground else AppColors.unCheckedBackground)
@@ -206,7 +207,7 @@ fun Agent() {
Text( Text(
text = stringResource(R.string.agent_other), text = stringResource(R.string.agent_other),
fontSize = 14.sp, fontSize = 14.sp,
color = if (pagerState.currentPage == 3) AppColors.checkedText else AppColors.text, color = if (pagerState.currentPage == 3) AppColors.mainText else AppColors.checkedBackground,
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 3) AppColors.checkedBackground else AppColors.unCheckedBackground) .background(if (pagerState.currentPage == 3) AppColors.checkedBackground else AppColors.unCheckedBackground)
@@ -231,7 +232,7 @@ fun Agent() {
} }
1 -> { 1 -> {
HotAgent()
} }
2 -> { 2 -> {

View File

@@ -0,0 +1,149 @@
package com.aiosman.ravenow.ui.index.tabs.ai.tabs.hot
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.composables.AgentCard
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun HotAgent() {
val AppColors = LocalAppTheme.current
val model = HotAgentViewModel
var agentList = model.agentList
val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
model.refreshPager(pullRefresh = true)
})
val listState = rememberLazyListState()
// observe list scrolling
val reachedBottom by remember {
derivedStateOf {
val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull()
lastVisibleItem?.index != 0 && lastVisibleItem?.index == listState.layoutInfo.totalItemsCount - 2
}
}
// load more if scrolled to bottom
LaunchedEffect(reachedBottom) {
if (reachedBottom && !model.isLoading && model.hasNext) {
model.loadMore()
}
}
LaunchedEffect(Unit) {
model.refreshPager()
}
Column(
modifier = Modifier
.fillMaxSize()
) {
if(agentList.isEmpty() && !model.isLoading) {
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Image(
painter = painterResource(id = R.mipmap.rider_pro_following_empty),
contentDescription = null,
modifier = Modifier.size(140.dp)
)
Spacer(modifier = Modifier.size(32.dp))
Text(
text = "您还没有创建任何智能体",
color = AppColors.text,
fontSize = 16.sp,
fontWeight = FontWeight.W600
)
Spacer(modifier = Modifier.size(16.dp))
Text(
text = "点击开始创建您的第一个智能体",
color = AppColors.text,
fontSize = 16.sp,
fontWeight = FontWeight.W400
)
}
}
} else {
Box(Modifier.pullRefresh(state)) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = listState
) {
items(
agentList.size,
key = { idx -> idx }
) { idx ->
val agentItem = agentList[idx]
AgentCard(agentEntity = agentItem)
}
// 加载更多指示器
if (model.isLoading && agentList.isNotEmpty()) {
item {
Box(
modifier = Modifier
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(
modifier = Modifier.size(24.dp),
color = AppColors.main
)
}
}
}
}
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
}
}
// 错误信息显示
model.error?.let { error ->
Box(
modifier = Modifier
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Text(
text = error,
color = AppColors.error,
fontSize = 14.sp
)
}
}
}
}

View File

@@ -0,0 +1,90 @@
package com.aiosman.ravenow.ui.index.tabs.ai.tabs.hot
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.data.ServiceException
import com.aiosman.ravenow.entity.AgentEntity
import kotlinx.coroutines.launch
object HotAgentViewModel : ViewModel() {
var agentList by mutableStateOf<List<AgentEntity>>(emptyList())
var refreshing by mutableStateOf(false)
var isLoading by mutableStateOf(false)
var hasNext by mutableStateOf(true)
var currentPage by mutableStateOf(1)
var error by mutableStateOf<String?>(null)
private val pageSize = 20
init {
refreshPager()
}
fun refreshPager(pullRefresh: Boolean = false) {
if (isLoading && !pullRefresh) return
viewModelScope.launch {
try {
isLoading = true
refreshing = pullRefresh
error = null
val response = ApiClient.api.getAgent(
page = 1,
pageSize = pageSize
)
val body = response.body()
if (body != null) {
val newAgents = body.data.list.map { it.toAgentEntity() }
agentList = newAgents
currentPage = 1
hasNext = newAgents.size == pageSize
} else {
throw ServiceException("Failed to load agents")
}
} catch (e: Exception) {
error = e.message ?: "加载失败"
e.printStackTrace()
} finally {
isLoading = false
refreshing = false
}
}
}
fun loadMore() {
if (isLoading || !hasNext) return
viewModelScope.launch {
try {
isLoading = true
error = null
val response = ApiClient.api.getAgent(
page = currentPage + 1,
pageSize = pageSize
)
val body = response.body()
if (body != null) {
val newAgents = body.data.list.map { it.toAgentEntity() }
agentList = agentList + newAgents
currentPage += 1
hasNext = newAgents.size == pageSize
} else {
throw ServiceException("Failed to load more agents")
}
} catch (e: Exception) {
error = e.message ?: "加载更多失败"
e.printStackTrace()
} finally {
isLoading = false
}
}
}
}

View File

@@ -5,10 +5,18 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import com.aiosman.ravenow.data.api.ApiClient import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.data.ServiceException import com.aiosman.ravenow.data.ServiceException
import com.aiosman.ravenow.data.api.AgentMomentRequestBody
import com.aiosman.ravenow.data.api.SingleChatRequestBody
import com.aiosman.ravenow.entity.AgentEntity import com.aiosman.ravenow.entity.AgentEntity
import com.aiosman.ravenow.ui.index.tabs.message.Conversation
import com.aiosman.ravenow.ui.index.tabs.message.MessageListViewModel.userService
import com.aiosman.ravenow.ui.navigateToChat
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
object MineAgentViewModel : ViewModel() { object MineAgentViewModel : ViewModel() {
var agentList by mutableStateOf<List<AgentEntity>>(emptyList()) var agentList by mutableStateOf<List<AgentEntity>>(emptyList())
@@ -87,4 +95,22 @@ object MineAgentViewModel : ViewModel() {
} }
} }
} }
suspend fun createSingleChat(
openId: String,
): String {
val response = ApiClient.api.createSingleChat(SingleChatRequestBody(generateText = openId))
val body = response.body()?.data ?: throw ServiceException("Failed to create single chat")
return body.toString()
}
fun goToChat(
conversation: Conversation,
navController: NavHostController
) {
viewModelScope.launch {
val profile = userService.getUserProfileByTrtcUserId(conversation.trtcUserId)
navController.navigateToChat(profile.id.toString())
}
}
} }