| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- import { joinLlmUrl, LLM_API_KEY } from '@/config/llm'
- import { mediaUrl, SENDER_ROLE_USER, SENDER_ROLE_AI } from '@/utils/aiConsult'
- export { SENDER_ROLE_USER, SENDER_ROLE_AI }
- export const MODEL_OPTION_DEFS = [
- { value: 'auto', labelKey: 'modelAuto', shortKey: 'modelAutoShort', descKey: 'modelAutoDesc', icon: 'grid-fill' },
- { value: 'yak-disease', labelKey: 'modelDisease', shortKey: 'modelDiseaseShort', descKey: 'modelDiseaseDesc', icon: 'order' },
- { value: 'yak-general', labelKey: 'modelGeneral', shortKey: 'modelGeneralShort', descKey: 'modelGeneralDesc', icon: 'chat' },
- { value: 'yak-feeding', labelKey: 'modelFeeding', shortKey: 'modelFeedingShort', descKey: 'modelFeedingDesc', icon: 'shopping-cart' },
- { value: 'yak-growth', labelKey: 'modelGrowth', shortKey: 'modelGrowthShort', descKey: 'modelGrowthDesc', icon: 'chat' }
- ]
- export const MEDIA_RULES = {
- image: { exts: ['jpg', 'jpeg', 'png', 'gif'], maxMb: 10, errFmt: 'errImageFmt', errMb: 'errImageMb' },
- video: { exts: ['mp4', 'mov'], maxMb: 50, errFmt: 'errVideoFmt', errMb: 'errVideoMb' },
- voice: { exts: ['mp3', 'm4a', 'wav'], maxMb: 10, errFmt: 'errVoiceFmt', errMb: 'errVoiceMb' }
- }
- export function genLocalId() {
- return 'm_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 9)
- }
- export function extOf(fileName) {
- if (!fileName || fileName.lastIndexOf('.') < 0) {
- return ''
- }
- return fileName.slice(fileName.lastIndexOf('.') + 1).toLowerCase()
- }
- /** 与大模型 SSE/JSON 一致:id 即为网关 sessionId */
- export function extractLlmSessionId(data) {
- if (!data || typeof data !== 'object') {
- return null
- }
- if (data.id != null && data.id !== '') {
- return String(data.id)
- }
- if (data.session_id != null && data.session_id !== '') {
- return String(data.session_id)
- }
- if (data.sessionId != null && data.sessionId !== '') {
- return String(data.sessionId)
- }
- return null
- }
- export function extractAssistantText(data) {
- try {
- const choice = data.choices && data.choices[0]
- const msg = choice && choice.message
- return (msg && msg.content) || ''
- } catch (e) {
- return ''
- }
- }
- export function contentForLlm(content) {
- if (typeof content === 'string') {
- return content
- }
- if (!Array.isArray(content)) {
- return ''
- }
- return content
- }
- export function buildUserContentForLlm(text, pendingAttachments) {
- const parts = []
- const imgs = (pendingAttachments || []).filter((a) => a.kind === 'image')
- for (const im of imgs) {
- parts.push({ type: 'image_url', image_url: { url: im.url } })
- }
- const body = (text || '').trim()
- if (body) {
- parts.push({ type: 'text', text: body })
- }
- if (!parts.length) {
- return ''
- }
- if (parts.length === 1 && parts[0].type === 'text') {
- return parts[0].text
- }
- return parts
- }
- export function buildMessagesForLlm(messages, llmSessionId) {
- if (llmSessionId) {
- for (let i = (messages || []).length - 1; i >= 0; i--) {
- const m = messages[i]
- if (m.senderRole === SENDER_ROLE_USER) {
- let content = m.content
- if (m.msgType === 2 && content) {
- content = [{ type: 'image_url', image_url: { url: mediaUrl(content) } }]
- }
- return [{ role: 'user', content: contentForLlm(content) }]
- }
- }
- return []
- }
- const out = []
- for (const m of messages || []) {
- const role = m.senderRole === SENDER_ROLE_AI ? 'assistant' : 'user'
- let content = m.content
- if (m.msgType === 2 && content) {
- content = [{ type: 'image_url', image_url: { url: mediaUrl(content) } }]
- }
- out.push({ role, content: contentForLlm(content) })
- }
- return out
- }
- export function resolvePayloadUserContent(payload, draft) {
- if (payload.msgType === 1) {
- return payload.content
- }
- if (payload.msgType === 2) {
- const parts = [{ type: 'image_url', image_url: { url: mediaUrl(payload.content) } }]
- const text = (draft || '').trim()
- if (text) {
- parts.push({ type: 'text', text })
- }
- return parts.length === 1 && parts[0].type === 'text' ? parts[0].text : parts
- }
- return String(payload.content || '')
- }
- export function buildPersistSendBody(msgType, payload, draftText, pendingAttachments) {
- const body = { msgType: msgType || 1 }
- if (body.msgType === 1) {
- let text =
- (draftText || '').trim() || (payload && payload.content ? String(payload.content).trim() : '')
- if (!text && pendingAttachments && pendingAttachments.length) {
- const img = pendingAttachments.find((a) => a.kind === 'image')
- if (img && img.url) {
- body.msgType = 2
- body.content = img.url
- return body
- }
- }
- body.content = text
- } else {
- body.content = (payload && payload.content) || ''
- if (payload && payload.mediaDuration != null) {
- body.mediaDuration = payload.mediaDuration
- }
- }
- return body
- }
- export function findLastLocalExchangeIndexes(messages) {
- let userIdx = -1
- let aiIdx = -1
- for (let i = (messages || []).length - 1; i >= 0; i--) {
- const m = messages[i]
- if (!m || !String(m.id).startsWith('m_')) {
- continue
- }
- if (aiIdx < 0 && m.senderRole === SENDER_ROLE_AI) {
- aiIdx = i
- } else if (userIdx < 0 && m.senderRole === SENDER_ROLE_USER) {
- userIdx = i
- }
- if (userIdx >= 0 && aiIdx >= 0) {
- break
- }
- }
- return { userIdx, aiIdx }
- }
- /**
- * 调用大模型 /v1/chat/completions
- */
- export function requestLlmChat(body) {
- const baseUrl = joinLlmUrl('')
- if (!baseUrl) {
- return Promise.reject(new Error('configLlmBase'))
- }
- if (!LLM_API_KEY) {
- return Promise.reject(new Error('configLlmKey'))
- }
- const header = {
- 'Content-Type': 'application/json',
- Authorization: 'Bearer ' + LLM_API_KEY
- }
- return new Promise((resolve, reject) => {
- uni.request({
- url: joinLlmUrl('/v1/chat/completions'),
- method: 'POST',
- header,
- data: body,
- timeout: 120000,
- success: (res) => {
- const httpStatus = res.statusCode || 200
- if (httpStatus >= 200 && httpStatus < 300) {
- const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
- resolve(data)
- return
- }
- let msg = 'requestFailed'
- try {
- const errBody = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
- msg = (errBody && (errBody.message || errBody.msg)) || msg
- } catch (e) {
- /* ignore */
- }
- reject(new Error(msg))
- },
- fail: (err) => reject(err || new Error('requestFailed'))
- })
- })
- }
|