智能体列表,全部

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

View File

@@ -38,6 +38,11 @@ data class AgentMomentRequestBody(
val sessionId: String
)
data class SingleChatRequestBody(
@SerializedName("agentOpenId")
val generateText: String,
)
data class LoginUserRequestBody(
@SerializedName("username")
val username: String? = null,
@@ -470,12 +475,14 @@ interface RaveNowAPI {
suspend fun getAgent(
@Query("page") page: Int = 1,
@Query("pageSize") pageSize: Int = 20,
): Response<ListContainer<Agent>>
@Query("withWorkflow") withWorkflow: Int = 1,
): Response<DataContainer<ListContainer<Agent>>>
@GET("outside/my/prompts")
suspend fun getMyAgent(
@Query("page") page: Int = 1,
@Query("pageSize") pageSize: Int = 20,
@Query("withWorkflow") withWorkflow: Int = 1,
): Response<ListContainer<Agent>>
@Multipart
@@ -483,12 +490,25 @@ interface RaveNowAPI {
suspend fun createAgent(
@Part avatar: MultipartBody.Part?,
@Part("title") title: RequestBody?,
@Part("value") value: 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>>
@POST("generate/postText")
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.UploadImage
import com.aiosman.ravenow.data.api.ApiClient
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import retrofit2.http.Part
import java.io.File
import java.io.IOException
/**
* 智能体
*/
suspend fun createAgent(
title: String,
desc: String,
avatar: UploadImage? = null,
workflowId:Int = 1,
isPublic:Boolean = true,
breakMode:Boolean = false,
useWorkflow:Boolean = true,
): AgentEntity {
val textTitle = title.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 {
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")
return body.toAgentEntity()
}
data class AgentEntity(
val author: String,
//val author: String,
val avatar: String,
val breakMode: Boolean,
val createdAt: String,
@@ -43,7 +56,7 @@ data class AgentEntity(
val id: Int,
val isPublic: Boolean,
val openId: String,
val profile: ProfileEntity,
//val profile: ProfileEntity,
val title: String,
val updatedAt: String,
val useCount: Int
@@ -80,8 +93,8 @@ class AgentLoader : DataLoader<AgentEntity,AgentLoaderExtraArgs>() {
)
val data = result.body()?.let {
ListContainer(
list = it.list.map { it.toAgentEntity()},
total = it.total,
list = it.data.list.map { it.toAgentEntity()},
total = it.data.total,
page = page,
pageSize = pageSize
)

View File

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

View File

@@ -80,7 +80,7 @@ fun AgentCard(
text = agentEntity.title,
color = AppColors.text,
fontSize = 16.sp,
style = TextStyle(fontWeight = FontWeight.Bold)
style = TextStyle(fontWeight = FontWeight.W700)
)
}
Row(
@@ -93,6 +93,7 @@ fun AgentCard(
modifier = Modifier,
text = agentEntity.desc,
color = AppColors.text,
maxLines = 1,
fontSize = 12.sp
)
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.ui.NavigationRoute
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 kotlinx.coroutines.launch
@@ -134,7 +135,7 @@ fun Agent() {
Text(
text = stringResource(R.string.agent_mine),
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
.clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 0) AppColors.checkedBackground else AppColors.unCheckedBackground)
@@ -158,7 +159,7 @@ fun Agent() {
Text(
text = stringResource(R.string.agent_hot),
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
.clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 1) AppColors.checkedBackground else AppColors.unCheckedBackground)
@@ -182,7 +183,7 @@ fun Agent() {
Text(
text = stringResource(R.string.agent_recommend),
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
.clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 2) AppColors.checkedBackground else AppColors.unCheckedBackground)
@@ -206,7 +207,7 @@ fun Agent() {
Text(
text = stringResource(R.string.agent_other),
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
.clip(RoundedCornerShape(8.dp))
.background(if (pagerState.currentPage == 3) AppColors.checkedBackground else AppColors.unCheckedBackground)
@@ -231,7 +232,7 @@ fun Agent() {
}
1 -> {
HotAgent()
}
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.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import com.aiosman.ravenow.data.api.ApiClient
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.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 okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
object MineAgentViewModel : ViewModel() {
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())
}
}
}