feat: restore android build path and update status docs

This commit is contained in:
kris
2026-03-20 14:17:33 +08:00
parent ac6a8a82df
commit 7070c3aa85
8 changed files with 1050 additions and 2 deletions

View File

@@ -0,0 +1,50 @@
package com.aiglasses.app.data
import kotlinx.serialization.json.Json
import kotlinx.serialization.ExperimentalSerializationApi
import java.util.concurrent.TimeUnit
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Protocol
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.create
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
object ApiClient {
@OptIn(ExperimentalSerializationApi::class)
val json = Json {
ignoreUnknownKeys = true
explicitNulls = false
}
inline fun <reified T : Any> createService(baseUrl: String): T {
val logging = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
val client = OkHttpClient.Builder()
.protocols(listOf(Protocol.HTTP_1_1))
.connectTimeout(12, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.callTimeout(25, TimeUnit.SECONDS)
.addInterceptor { chain ->
val request: Request = chain.request().newBuilder()
.header("Connection", "close")
.build()
chain.proceed(request)
}
.addInterceptor(logging)
.build()
val normalizedBaseUrl = if (baseUrl.endsWith("/")) baseUrl else "$baseUrl/"
return Retrofit.Builder()
.baseUrl(normalizedBaseUrl)
.client(client)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
.create<T>()
}
}

View File

@@ -0,0 +1,154 @@
package com.aiglasses.app.data
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
interface ApiService {
@GET("/healthz")
suspend fun healthz(): ApiEnvelope<HealthzData>
@POST("/api/v1/devices/bind-confirm")
suspend fun bindConfirm(
@Body request: BindConfirmRequest
): ApiEnvelope<BindConfirmData>
@POST("/api/v1/ai/sessions")
suspend fun createSession(
@Header("Idempotency-Key") idempotencyKey: String?,
@Body request: CreateSessionRequest
): ApiEnvelope<SessionData>
@POST("/api/v1/ai/sessions/{sessionId}/stop")
suspend fun stopSession(
@Path("sessionId") sessionId: String,
@Body request: StopSessionRequest
): ApiEnvelope<StopSessionData>
@POST("/api/v1/ai/sessions/{sessionId}/heartbeat")
suspend fun heartbeat(
@Path("sessionId") sessionId: String,
@Body request: HeartbeatRequest
): ApiEnvelope<HeartbeatData>
@GET("/api/v1/devices/{deviceId}/status")
suspend fun getDeviceStatus(
@Path("deviceId") deviceId: String
): ApiEnvelope<DeviceStatusData>
@POST("/api/v1/events")
suspend fun postEvent(
@Body request: ClientEventRequest
): ApiEnvelope<EventSavedData>
@POST("/api/v1/events/batch")
suspend fun postEventsBatch(
@Body request: ClientEventBatchRequest
): ApiEnvelope<EventsBatchSavedData>
@POST("/api/v1/ai/sessions/{sessionId}/messages")
suspend fun sendMessage(
@Path("sessionId") sessionId: String,
@Body request: SessionMessageRequest
): ApiEnvelope<ProviderActionData>
@POST("/api/v1/ai/sessions/{sessionId}/scene-role")
suspend fun switchRole(
@Path("sessionId") sessionId: String,
@Body request: SwitchRoleRequest
): ApiEnvelope<ProviderActionData>
@POST("/api/v1/ai/sessions/{sessionId}/interrupt")
suspend fun interruptSession(
@Path("sessionId") sessionId: String,
@Body request: SessionInterruptRequest
): ApiEnvelope<ProviderActionData>
@GET("/api/v1/baidu/activation/query")
suspend fun activationQuery(
@Query("deviceId") deviceId: String,
@Query("appId") appId: String? = null
): ApiEnvelope<ActivationQueryData>
@POST("/api/v1/licenses/reload")
suspend fun reloadLicenses(): ApiEnvelope<ReloadLicensesData>
@GET("/api/v1/admin/overview")
suspend fun adminOverview(): ApiEnvelope<AdminOverviewData>
@GET("/api/v1/app/update/latest")
suspend fun appUpdateLatest(
@Query("platform") platform: String = "android",
@Query("channel") channel: String = "stable",
@Query("currentVersionCode") currentVersionCode: Int
): ApiEnvelope<AppUpdateLatestData>
@GET("/v2/douyin/accounts")
suspend fun listDouyinAccounts(): ApiEnvelope<List<DouyinAccountSummary>>
@POST("/v2/douyin/accounts/sync")
suspend fun syncDouyinAccount(
@Body request: DouyinAccountSyncRequest
): ApiEnvelope<DouyinAccountWorkspace>
@GET("/v2/douyin/accounts/{accountId}")
suspend fun getDouyinAccount(
@Path("accountId") accountId: String
): ApiEnvelope<DouyinAccountWorkspace>
@GET("/v2/douyin/accounts/{accountId}/workspace")
suspend fun getDouyinWorkspace(
@Path("accountId") accountId: String
): ApiEnvelope<DouyinAccountWorkspace>
@GET("/v2/douyin/accounts/{accountId}/snapshots")
suspend fun listDouyinSnapshots(
@Path("accountId") accountId: String
): ApiEnvelope<List<DouyinSnapshotSummary>>
@GET("/v2/douyin/accounts/{accountId}/snapshots/{snapshotId}")
suspend fun getDouyinSnapshot(
@Path("accountId") accountId: String,
@Path("snapshotId") snapshotId: String
): ApiEnvelope<DouyinSnapshotDetail>
@GET("/v2/douyin/accounts/{accountId}/creator-fields")
suspend fun getDouyinCreatorFields(
@Path("accountId") accountId: String
): ApiEnvelope<DouyinSnapshotDetail>
@POST("/v2/douyin/accounts/{accountId}/analysis")
suspend fun analyzeDouyinAccount(
@Path("accountId") accountId: String,
@Body request: DouyinAccountAnalysisRequest
): ApiEnvelope<DouyinAnalysisResult>
@GET("/v2/douyin/accounts/{accountId}/analysis-reports")
suspend fun listDouyinAnalysisReports(
@Path("accountId") accountId: String
): ApiEnvelope<List<DouyinAnalysisReport>>
@POST("/v2/douyin/similar-searches")
suspend fun createDouyinSimilarSearch(
@Body request: DouyinSimilarSearchRequest
): ApiEnvelope<DouyinSimilaritySearchResult>
@GET("/v2/douyin/similar-searches/{searchId}")
suspend fun getDouyinSimilarSearch(
@Path("searchId") searchId: String
): ApiEnvelope<DouyinSimilaritySearchDetail>
@GET("/v2/douyin/accounts/{accountId}/benchmark-links")
suspend fun listDouyinBenchmarkLinks(
@Path("accountId") accountId: String
): ApiEnvelope<List<DouyinLinkedAccount>>
@POST("/v2/douyin/accounts/{accountId}/benchmark-links")
suspend fun createDouyinBenchmarkLinks(
@Path("accountId") accountId: String,
@Body request: DouyinBenchmarkLinkRequest
): ApiEnvelope<DouyinBenchmarkLinkResult>
}

View File

@@ -0,0 +1,276 @@
package com.aiglasses.app.data
import java.util.UUID
class BackendRepository(private var baseUrl: String) {
private var api: ApiService = ApiClient.createService(baseUrl)
fun updateBaseUrl(url: String) {
if (url != baseUrl) {
baseUrl = url
api = ApiClient.createService(baseUrl)
}
}
suspend fun bindDevice(deviceId: String, userId: String): BindConfirmData {
val resp = api.bindConfirm(BindConfirmRequest(deviceId = deviceId, appUserId = userId))
return resp.data
}
suspend fun healthz(): HealthzData {
val resp = api.healthz()
return resp.data
}
suspend fun createSession(deviceId: String, userId: String): SessionData {
val idempotencyKey = "app-${UUID.randomUUID()}"
val resp = api.createSession(
idempotencyKey = idempotencyKey,
request = CreateSessionRequest(deviceId = deviceId, appUserId = userId)
)
return resp.data
}
suspend fun stopSession(sessionId: String): StopSessionData {
val resp = api.stopSession(sessionId, StopSessionRequest())
return resp.data
}
suspend fun heartbeat(sessionId: String): HeartbeatData {
val resp = api.heartbeat(sessionId, HeartbeatRequest())
return resp.data
}
suspend fun getDeviceStatus(deviceId: String): DeviceStatusData {
val resp = api.getDeviceStatus(deviceId)
return resp.data
}
suspend fun postDemoEvent(deviceId: String, sessionId: String?): EventSavedData {
return postEvent(
deviceId = deviceId,
sessionId = sessionId,
eventType = "APP_DEBUG_PING",
eventLevel = "INFO",
payload = mapOf("source" to "android")
)
}
suspend fun postEvent(
deviceId: String,
sessionId: String?,
eventType: String,
eventLevel: String = "INFO",
payload: Map<String, String> = emptyMap()
): EventSavedData {
val resp = api.postEvent(
ClientEventRequest(
sessionId = sessionId,
deviceId = deviceId,
eventType = eventType,
eventLevel = eventLevel,
payload = payload
)
)
return resp.data
}
suspend fun postEventsBatch(events: List<ClientEventRequest>): EventsBatchSavedData {
val resp = api.postEventsBatch(ClientEventBatchRequest(events = events))
return resp.data
}
suspend fun sendMessage(sessionId: String, message: String): ProviderActionData {
val resp = api.sendMessage(
sessionId = sessionId,
request = SessionMessageRequest(message = message)
)
return resp.data
}
suspend fun sendVoiceMessage(
sessionId: String,
pcmBase64: String,
sampleRate: Int,
durationMs: Int,
rms: Int
): ProviderActionData {
val resp = api.sendMessage(
sessionId = sessionId,
request = SessionMessageRequest(
message = "voice_chunk",
messageType = "voice",
extra = mapOf(
"audio_base64" to pcmBase64,
"audio_format" to "pcm_s16le",
"sample_rate" to sampleRate.toString(),
"channels" to "1",
"duration_ms" to durationMs.toString(),
"rms" to rms.toString(),
"encoding" to "base64"
)
)
)
return resp.data
}
suspend fun sendVisionMessage(
sessionId: String,
message: String,
imageBase64: String,
width: Int,
height: Int,
bytes: Int
): ProviderActionData {
val resp = api.sendMessage(
sessionId = sessionId,
request = SessionMessageRequest(
message = message,
messageType = "text",
extra = mapOf(
"image_base64" to imageBase64,
"imageBase64" to imageBase64,
"image" to imageBase64,
"resource_base64" to imageBase64,
"resourceBase64" to imageBase64,
"image_encoding" to "base64",
"imageEncoding" to "base64",
"encoding" to "base64",
"image_format" to "jpeg",
"imageFormat" to "jpeg",
"mime_type" to "image/jpeg",
"mimeType" to "image/jpeg",
"image_width" to width.toString(),
"imageWidth" to width.toString(),
"image_height" to height.toString(),
"imageHeight" to height.toString(),
"image_bytes" to bytes.toString(),
"imageBytes" to bytes.toString(),
"resource_type" to "image",
"resourceType" to "image",
"camera_source" to "android_phone",
"multimodal" to "true",
"with_vision" to "1"
)
)
)
return resp.data
}
suspend fun switchRole(sessionId: String, sceneId: String, roleId: String): ProviderActionData {
val resp = api.switchRole(
sessionId = sessionId,
request = SwitchRoleRequest(sceneId = sceneId, roleId = roleId)
)
return resp.data
}
suspend fun interrupt(
sessionId: String,
interrupt: Boolean,
extra: Map<String, String> = emptyMap()
): ProviderActionData {
val resp = api.interruptSession(
sessionId = sessionId,
request = SessionInterruptRequest(interrupt = interrupt, extra = extra)
)
return resp.data
}
suspend fun activationQuery(deviceId: String): ActivationQueryData {
val resp = api.activationQuery(deviceId = deviceId)
return resp.data
}
suspend fun reloadLicenses(): ReloadLicensesData {
val resp = api.reloadLicenses()
return resp.data
}
suspend fun adminOverview(): AdminOverviewData {
val resp = api.adminOverview()
return resp.data
}
suspend fun appUpdateLatest(currentVersionCode: Int): AppUpdateLatestData {
val resp = api.appUpdateLatest(
platform = "android",
channel = "stable",
currentVersionCode = currentVersionCode
)
return resp.data
}
suspend fun listDouyinAccounts(): List<DouyinAccountSummary> {
val resp = api.listDouyinAccounts()
return resp.data
}
suspend fun syncDouyinAccount(request: DouyinAccountSyncRequest): DouyinAccountWorkspace {
val resp = api.syncDouyinAccount(request)
return resp.data
}
suspend fun getDouyinAccount(accountId: String): DouyinAccountWorkspace {
val resp = api.getDouyinAccount(accountId)
return resp.data
}
suspend fun getDouyinWorkspace(accountId: String): DouyinAccountWorkspace {
val resp = api.getDouyinWorkspace(accountId)
return resp.data
}
suspend fun listDouyinSnapshots(accountId: String): List<DouyinSnapshotSummary> {
val resp = api.listDouyinSnapshots(accountId)
return resp.data
}
suspend fun getDouyinSnapshot(accountId: String, snapshotId: String): DouyinSnapshotDetail {
val resp = api.getDouyinSnapshot(accountId, snapshotId)
return resp.data
}
suspend fun getDouyinCreatorFields(accountId: String): DouyinSnapshotDetail {
val resp = api.getDouyinCreatorFields(accountId)
return resp.data
}
suspend fun analyzeDouyinAccount(
accountId: String,
request: DouyinAccountAnalysisRequest
): DouyinAnalysisResult {
val resp = api.analyzeDouyinAccount(accountId, request)
return resp.data
}
suspend fun listDouyinAnalysisReports(accountId: String): List<DouyinAnalysisReport> {
val resp = api.listDouyinAnalysisReports(accountId)
return resp.data
}
suspend fun createDouyinSimilarSearch(
request: DouyinSimilarSearchRequest
): DouyinSimilaritySearchResult {
val resp = api.createDouyinSimilarSearch(request)
return resp.data
}
suspend fun getDouyinSimilarSearch(searchId: String): DouyinSimilaritySearchDetail {
val resp = api.getDouyinSimilarSearch(searchId)
return resp.data
}
suspend fun listDouyinBenchmarkLinks(accountId: String): List<DouyinLinkedAccount> {
val resp = api.listDouyinBenchmarkLinks(accountId)
return resp.data
}
suspend fun createDouyinBenchmarkLinks(
accountId: String,
request: DouyinBenchmarkLinkRequest
): DouyinBenchmarkLinkResult {
val resp = api.createDouyinBenchmarkLinks(accountId, request)
return resp.data
}
}

View File

@@ -0,0 +1,540 @@
package com.aiglasses.app.data
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
@Serializable
data class ApiEnvelope<T>(
val code: Int,
val message: String,
val traceId: String,
val data: T
)
@Serializable
data class HealthzData(
val status: String = "",
val env: String = "",
val dbPath: String = ""
)
@Serializable
data class BindConfirmRequest(
val deviceId: String,
val deviceSn: String? = null,
val deviceModel: String? = null,
val deviceFwVer: String? = null,
val appUserId: String
)
@Serializable
data class BindConfirmData(
val bindStatus: String,
val licenseStatus: String,
val licenseKeyMasked: String,
val licenseKey: String = ""
)
@Serializable
data class CreateSessionRequest(
val deviceId: String,
val appUserId: String,
val scene: String = "voice_assistant",
val language: String = "zh-CN",
val clientTs: Long? = null
)
@Serializable
data class SessionData(
val sessionId: String,
val provider: String,
val cid: String,
val token: String,
val tokenExpireAt: Long,
val wsUrl: String,
val heartbeatSec: Int,
val appId: String = "",
val context: String = "",
val realtimeWsUrl: String = ""
)
@Serializable
data class StopSessionRequest(
val reason: String = "user_stop"
)
@Serializable
data class StopSessionData(
val sessionStatus: String
)
@Serializable
data class HeartbeatRequest(
val networkType: String? = "wifi",
val bleRssi: Int? = null
)
@Serializable
data class HeartbeatData(
val sessionStatus: String,
val heartbeatAt: Long
)
@Serializable
data class ClientEventRequest(
val sessionId: String? = null,
val deviceId: String,
val eventType: String,
val eventLevel: String = "INFO",
val payload: Map<String, String> = emptyMap(),
val ts: Long? = null
)
@Serializable
data class ClientEventBatchRequest(
val events: List<ClientEventRequest> = emptyList()
)
@Serializable
data class EventSavedData(
val saved: Boolean
)
@Serializable
data class EventsBatchSavedData(
val saved: Int = 0
)
@Serializable
data class SessionMessageRequest(
val message: String,
val messageType: String = "text",
val messageId: String? = null,
val extra: Map<String, String> = emptyMap()
)
@Serializable
data class ProviderActionData(
val status: String = "UNKNOWN",
val detail: String = "",
val asrText: String = "",
val ttsText: String = "",
val audioBase64: String = "",
val audioUrl: String = ""
)
@Serializable
data class SwitchRoleRequest(
val sceneId: String,
val roleId: String,
val extra: Map<String, String> = emptyMap()
)
@Serializable
data class SessionInterruptRequest(
val interrupt: Boolean = true,
val extra: Map<String, String> = emptyMap()
)
@Serializable
data class DeviceStatusData(
val bindStatus: String,
val licenseStatus: String,
val activeSessionId: String? = null,
val activeSessionStatus: String? = null
)
@Serializable
data class AdminStats(
val totalDevices: Int = 0,
val totalSessions: Int = 0,
val runningSessions: Int = 0,
val totalLicenses: Int = 0,
val usedLicenseQuota: Int = 0
)
@Serializable
data class BaiduInfo(
val mode: String = "-",
val generateConfigured: Boolean = false,
val stopConfigured: Boolean = false,
val activationQueryConfigured: Boolean = false
)
@Serializable
data class AdminOverviewData(
val stats: AdminStats = AdminStats(),
val baidu: BaiduInfo = BaiduInfo()
)
@Serializable
data class ActivationQueryData(
val deviceId: String = "",
val appId: String = "",
val status: String = "UNKNOWN",
val detail: String = "",
val licenseKeyMasked: String = ""
)
@Serializable
data class ReloadLicensesData(
val inserted: Int = 0
)
@Serializable
data class AppUpdateLatestData(
val platform: String = "android",
val channel: String = "stable",
val hasUpdate: Boolean = false,
val latestVersionCode: Int = 0,
val latestVersionName: String = "",
val minSupportedCode: Int = 0,
val downloadUrl: String = "",
val apkSha256: String = "",
val releaseNotes: String = "",
val forceUpdate: Boolean = false,
val publishedAt: Long = 0L
)
@Serializable
data class DouyinManualPageCaptureRequest(
val url: String = "",
val title: String = "",
val payload: JsonObject = JsonObject(emptyMap())
)
@Serializable
data class DouyinAccountSyncRequest(
@SerialName("profile_url")
val profileUrl: String = "",
@SerialName("session_cookie")
val sessionCookie: String = "",
@SerialName("creator_center_urls")
val creatorCenterUrls: List<String> = emptyList(),
@SerialName("manual_profile_payload")
val manualProfilePayload: JsonObject? = null,
@SerialName("manual_creator_pages")
val manualCreatorPages: List<DouyinManualPageCaptureRequest> = emptyList(),
@SerialName("manual_work_payloads")
val manualWorkPayloads: List<JsonObject> = emptyList(),
@SerialName("discovery_note")
val discoveryNote: String = ""
)
@Serializable
data class DouyinProfileStats(
val followers: Double = 0.0,
val following: Double = 0.0,
val likes: Double = 0.0,
val videos: Double = 0.0
)
@Serializable
data class DouyinVideoStats(
val play: Double = 0.0,
val like: Double = 0.0,
val comment: Double = 0.0,
val share: Double = 0.0,
val collect: Double = 0.0
)
@Serializable
data class DouyinVideoSummaryItem(
@SerialName("aweme_id")
val awemeId: String = "",
val title: String = "",
val description: String = "",
val tags: List<String> = emptyList(),
@SerialName("published_at")
val publishedAt: String? = null,
val stats: DouyinVideoStats = DouyinVideoStats()
)
@Serializable
data class DouyinVideoSummary(
val count: Int = 0,
@SerialName("top_tags")
val topTags: List<String> = emptyList(),
@SerialName("avg_play")
val avgPlay: Double = 0.0,
@SerialName("avg_like")
val avgLike: Double = 0.0,
@SerialName("avg_comment")
val avgComment: Double = 0.0,
@SerialName("avg_share")
val avgShare: Double = 0.0,
val videos: List<DouyinVideoSummaryItem> = emptyList()
)
@Serializable
data class DouyinAccountSummary(
val id: String = "",
val nickname: String = "",
val signature: String = "",
@SerialName("profile_url")
val profileUrl: String = "",
@SerialName("avatar_url")
val avatarUrl: String = "",
@SerialName("sec_uid")
val secUid: String = "",
@SerialName("douyin_id")
val douyinId: String = "",
@SerialName("profile_stats")
val profileStats: DouyinProfileStats = DouyinProfileStats(),
val tags: List<String> = emptyList(),
val keywords: List<String> = emptyList(),
@SerialName("sync_status")
val syncStatus: String = "",
@SerialName("video_summary")
val videoSummary: DouyinVideoSummary = DouyinVideoSummary()
)
@Serializable
data class DouyinSnapshotSummary(
val id: String = "",
@SerialName("snapshot_type")
val snapshotType: String = "",
@SerialName("source_url")
val sourceUrl: String = "",
@SerialName("field_count")
val fieldCount: Int = 0,
@SerialName("collected_at")
val collectedAt: String = "",
val summary: JsonObject = JsonObject(emptyMap())
)
@Serializable
data class DouyinModelProfileSummary(
val id: String = "",
val name: String = "",
@SerialName("model_name")
val modelName: String = "",
@SerialName("base_url")
val baseUrl: String = "",
@SerialName("is_default")
val isDefault: Boolean = false
)
@Serializable
data class DouyinAnalysisSuggestion(
val id: String = "",
@SerialName("model_profile_id")
val modelProfileId: String = "",
@SerialName("model_label")
val modelLabel: String = "",
val status: String = "",
@SerialName("suggestion_text")
val suggestionText: String = "",
@SerialName("parsed_json")
val parsedJson: JsonElement = JsonObject(emptyMap())
)
@Serializable
data class DouyinAnalysisReport(
val id: String = "",
@SerialName("focus_text")
val focusText: String = "",
@SerialName("model_profile_ids")
val modelProfileIds: List<String> = emptyList(),
@SerialName("linked_account_ids")
val linkedAccountIds: List<String> = emptyList(),
@SerialName("created_at")
val createdAt: String = "",
val suggestions: List<DouyinAnalysisSuggestion> = emptyList()
)
@Serializable
data class DouyinSimilaritySearchPreview(
val id: String = "",
val keywords: List<String> = emptyList(),
@SerialName("created_at")
val createdAt: String = ""
)
@Serializable
data class DouyinLinkedAccount(
@SerialName("relation_id")
val relationId: String = "",
@SerialName("relation_type")
val relationType: String = "",
val note: String = "",
@SerialName("search_id")
val searchId: String = "",
@SerialName("created_at")
val createdAt: String = "",
@SerialName("target_account_id")
val targetAccountId: String? = null,
@SerialName("target_profile_url")
val targetProfileUrl: String = "",
@SerialName("target_nickname")
val targetNickname: String = "",
@SerialName("target_signature")
val targetSignature: String = "",
@SerialName("target_profile_stats")
val targetProfileStats: DouyinProfileStats = DouyinProfileStats(),
@SerialName("target_tags")
val targetTags: List<String> = emptyList()
)
@Serializable
data class DouyinAccountWorkspace(
val account: DouyinAccountSummary = DouyinAccountSummary(),
@SerialName("latest_public_snapshot")
val latestPublicSnapshot: DouyinSnapshotSummary? = null,
@SerialName("latest_creator_snapshot")
val latestCreatorSnapshot: DouyinSnapshotSummary? = null,
@SerialName("linked_accounts")
val linkedAccounts: List<DouyinLinkedAccount> = emptyList(),
@SerialName("recent_reports")
val recentReports: List<DouyinAnalysisReport> = emptyList(),
@SerialName("recent_similarity_searches")
val recentSimilaritySearches: List<DouyinSimilaritySearchPreview> = emptyList(),
@SerialName("available_model_profiles")
val availableModelProfiles: List<DouyinModelProfileSummary> = emptyList(),
@SerialName("sync_errors")
val syncErrors: List<String> = emptyList()
)
@Serializable
data class DouyinAccountAnalysisRequest(
@SerialName("model_profile_ids")
val modelProfileIds: List<String> = emptyList(),
@SerialName("linked_account_ids")
val linkedAccountIds: List<String> = emptyList(),
@SerialName("include_linked_accounts")
val includeLinkedAccounts: Boolean = true,
@SerialName("include_recent_similar_candidates")
val includeRecentSimilarCandidates: Boolean = true,
@SerialName("max_videos")
val maxVideos: Int = 12,
@SerialName("extra_focus")
val extraFocus: String = "",
val temperature: Double = 0.35
)
@Serializable
data class DouyinAnalysisResult(
@SerialName("report_id")
val reportId: String = "",
@SerialName("created_at")
val createdAt: String = "",
val context: JsonElement = JsonObject(emptyMap()),
val suggestions: List<DouyinAnalysisSuggestion> = emptyList()
)
@Serializable
data class DouyinSimilarSearchRequest(
@SerialName("source_account_id")
val sourceAccountId: String? = null,
@SerialName("profile_url")
val profileUrl: String? = null,
@SerialName("candidate_urls")
val candidateUrls: List<String> = emptyList(),
@SerialName("seed_linked_accounts")
val seedLinkedAccounts: Boolean = true,
@SerialName("search_public_pages")
val searchPublicPages: Boolean = true,
@SerialName("model_profile_id")
val modelProfileId: String? = null,
@SerialName("max_candidates")
val maxCandidates: Int = 10,
@SerialName("extra_requirements")
val extraRequirements: String = ""
)
@Serializable
data class DouyinSimilarCandidate(
val id: String = "",
@SerialName("candidate_account_id")
val candidateAccountId: String? = null,
@SerialName("candidate_profile_url")
val candidateProfileUrl: String = "",
@SerialName("candidate_nickname")
val candidateNickname: String = "",
@SerialName("heuristic_score")
val heuristicScore: Double = 0.0,
@SerialName("agent_score")
val agentScore: Double = 0.0,
@SerialName("rationale_text")
val rationaleText: String = "",
val dimensions: JsonElement = JsonObject(emptyMap()),
@SerialName("rank_index")
val rankIndex: Int = 0
)
@Serializable
data class DouyinSimilaritySearchResult(
@SerialName("search_id")
val searchId: String = "",
@SerialName("source_account")
val sourceAccount: DouyinAccountSummary = DouyinAccountSummary(),
@SerialName("model_profile")
val modelProfile: JsonObject = JsonObject(emptyMap()),
@SerialName("raw_model_output")
val rawModelOutput: String = "",
val candidates: List<DouyinSimilarCandidate> = emptyList()
)
@Serializable
data class DouyinSimilaritySearchDetail(
val id: String = "",
@SerialName("source_account_id")
val sourceAccountId: String? = null,
@SerialName("source_profile_url")
val sourceProfileUrl: String = "",
val keywords: List<String> = emptyList(),
val context: JsonElement = JsonObject(emptyMap()),
@SerialName("created_at")
val createdAt: String = "",
val candidates: List<DouyinSimilarCandidate> = emptyList()
)
@Serializable
data class DouyinBenchmarkLinkRequest(
@SerialName("target_account_ids")
val targetAccountIds: List<String> = emptyList(),
@SerialName("target_profile_urls")
val targetProfileUrls: List<String> = emptyList(),
@SerialName("relation_type")
val relationType: String = "benchmark",
val note: String = "",
@SerialName("search_id")
val searchId: String = ""
)
@Serializable
data class DouyinBenchmarkLinkResult(
val saved: Int = 0,
@SerialName("relation_ids")
val relationIds: List<String> = emptyList(),
val links: List<DouyinLinkedAccount> = emptyList()
)
@Serializable
data class DouyinSnapshotField(
@SerialName("field_path")
val fieldPath: String = "",
@SerialName("field_type")
val fieldType: String = "",
@SerialName("field_value_text")
val fieldValueText: String = ""
)
@Serializable
data class DouyinSnapshotDetail(
val id: String = "",
@SerialName("snapshot_type")
val snapshotType: String = "",
@SerialName("source_url")
val sourceUrl: String = "",
@SerialName("field_count")
val fieldCount: Int = 0,
@SerialName("collected_at")
val collectedAt: String = "",
val summary: JsonObject = JsonObject(emptyMap()),
@SerialName("raw_payload")
val rawPayload: JsonElement = JsonObject(emptyMap()),
val fields: List<DouyinSnapshotField> = emptyList()
)