-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgeminiService.ts
More file actions
146 lines (132 loc) · 5.23 KB
/
Copy pathgeminiService.ts
File metadata and controls
146 lines (132 loc) · 5.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import { GoogleGenAI, Type, Modality } from "@google/genai";
const getAI = () => new GoogleGenAI({ apiKey: process.env.API_KEY || '' });
export const analyzeMedicationImage = async (base64Image: string) => {
const ai = getAI();
const prompt = `分析这张药品图片。请使用中文提取以下详细信息并以 JSON 格式输出:
- name: 字符串 (药品名称)
- type: "处方药" | "保健品" | "非处方药" | "注射剂"
- dosage: 字符串 (例如 500mg)
- instructions: 字符串 (用法用量说明)
- withFood: 布尔值 (是否随餐服用)
- sideEffects: 字符串数组 (潜在副作用列表)
- priority: "普通" | "重要" | "紧急"
- reminderTimes: 字符串数组 (根据 frequency 建议合理的服药时间点,如 ["08:00"])
请确保严格遵循 JSON 结构。`;
try {
const response = await ai.models.generateContent({
model: "gemini-3-pro-preview",
contents: {
parts: [
{ inlineData: { data: base64Image, mimeType: "image/jpeg" } },
{ text: prompt }
]
},
config: {
responseMimeType: "application/json",
responseSchema: {
type: Type.OBJECT,
properties: {
name: { type: Type.STRING },
type: { type: Type.STRING },
dosage: { type: Type.STRING },
instructions: { type: Type.STRING },
withFood: { type: Type.BOOLEAN },
sideEffects: { type: Type.ARRAY, items: { type: Type.STRING } },
priority: { type: Type.STRING },
reminderTimes: { type: Type.ARRAY, items: { type: Type.STRING } }
}
}
}
});
return JSON.parse(response.text || '{}');
} catch (error) {
console.error("Gemini 分析错误:", error);
return null;
}
};
export const askAboutMedication = async (medName: string, question: string) => {
const ai = getAI();
const prompt = `关于药物 "${medName}",用户想知道:${question}。
请作为专业的健康助手提供简明的中文解答,必须包含免责声明:本回复仅供 AI 参考,具体请遵医嘱。
字数控制在 150 字以内。`;
try {
const response = await ai.models.generateContent({
model: "gemini-3-flash-preview",
contents: prompt,
});
return response.text;
} catch (error) {
console.error("AI 问答错误:", error);
return "抱歉,由于网络原因暂时无法回答该问题。";
}
};
export const checkDrugInteractions = async (newMed: string, existingMeds: string[]) => {
const ai = getAI();
const prompt = `我准备服用新药 "${newMed}"。我目前正在服用的药物有:${existingMeds.join(', ')}。
请分析这些药物之间是否存在明显的药性相冲或相互作用。
如果存在风险,请提供简短的中文警告信息;如果没有明显冲突,请回复“暂未发现明显药物冲突”。
字数请控制在 100 字以内。`;
try {
const response = await ai.models.generateContent({
model: "gemini-3-flash-preview",
contents: prompt,
});
return response.text || "无法获取药性相冲信息。";
} catch (error) {
console.error("冲突检查错误:", error);
return "由于网络原因,无法检查药性相冲情况。";
}
};
export const playMedReminder = async (medName: string) => {
const ai = getAI();
const text = `该吃药啦,这是您的 ${medName}。请记得核对用量。`;
try {
const response = await ai.models.generateContent({
model: "gemini-2.5-flash-preview-tts",
contents: [{ parts: [{ text }] }],
config: {
responseModalities: [Modality.AUDIO],
speechConfig: {
voiceConfig: {
prebuiltVoiceConfig: { voiceName: 'Kore' },
},
},
},
});
const base64Audio = response.candidates?.[0]?.content?.parts?.[0]?.inlineData?.data;
if (base64Audio) {
const audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)({ sampleRate: 24000 });
const decoded = decode(base64Audio);
const buffer = await decodeAudioData(decoded, audioCtx, 24000, 1);
const source = audioCtx.createBufferSource();
source.buffer = buffer;
source.connect(audioCtx.destination);
source.start();
}
} catch (error) {
console.error("Gemini TTS 错误:", error);
const msg = new SpeechSynthesisUtterance(text);
msg.lang = 'zh-CN';
window.speechSynthesis.speak(msg);
}
};
function decode(base64: string) {
const binaryString = atob(base64);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes;
}
async function decodeAudioData(data: Uint8Array, ctx: AudioContext, sampleRate: number, numChannels: number): Promise<AudioBuffer> {
const dataInt16 = new Int16Array(data.buffer);
const frameCount = dataInt16.length / numChannels;
const buffer = ctx.createBuffer(numChannels, frameCount, sampleRate);
for (let channel = 0; channel < numChannels; channel++) {
const channelData = buffer.getChannelData(channel);
for (let i = 0; i < frameCount; i++) {
channelData[i] = dataInt16[i * numChannels + channel] / 32768.0;
}
}
return buffer;
}