File size: 2,289 Bytes
35d8a1a
 
 
 
 
9316fdd
16e01d6
 
6001a3f
6c13d95
16e01d6
70de7cf
 
2246057
70de7cf
5fc28d8
4214d41
35d8a1a
6c13d95
 
 
 
 
 
 
 
 
 
5eaa3c8
35d8a1a
2246057
 
35d8a1a
16e01d6
824fa65
35d8a1a
2246057
8a56152
 
824fa65
 
 
 
35d8a1a
 
9316fdd
 
 
 
 
 
 
 
 
 
 
 
 
6001a3f
9316fdd
 
 
 
 
6001a3f
 
70de7cf
6001a3f
2246057
9316fdd
8350281
824fa65
35d8a1a
824fa65
16e01d6
70de7cf
 
 
 
 
 
 
 
 
 
4a3e249
70de7cf
 
 
 
 
 
 
 
 
82d921f
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
import io
import numpy as np
import soundfile as sf
import edge_tts
import miniaudio

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from scipy.signal import resample
from pydantic import BaseModel

from ltm import EmbeddingMemory

app = FastAPI()
memory = EmbeddingMemory()

VOICE = "pl-PL-ZofiaNeural"

class MemoryMessage(BaseModel):
    session_id: str
    role: str
    content: str

class MemoryQuery(BaseModel):
    session_id: str | None = None
    query: str
    top_k: int = 5

@app.post("/tts")
async def tts(data: dict):
    text = data.get("text", "").strip()
    if not text:
        return {"error": "empty text"}

    mp3_buffer = io.BytesIO()
    communicate = edge_tts.Communicate(text, VOICE)

    async for chunk in communicate.stream():
        if chunk["type"] == "audio":
            mp3_buffer.write(chunk["data"])

    mp3_buffer.seek(0)

    decoded = miniaudio.decode(mp3_buffer.read())

    raw = decoded.samples

    if decoded.sample_format == miniaudio.SampleFormat.SIGNED16:
        audio = np.frombuffer(raw, dtype=np.int16).astype(np.float32) / 32768.0

    elif decoded.sample_format == miniaudio.SampleFormat.FLOAT32:
        audio = np.frombuffer(raw, dtype=np.float32)

    else:
        return {"error": "Unsupported audio format from decoder"}

    if decoded.nchannels > 1:
        audio = audio.reshape(-1, decoded.nchannels)
        audio = np.mean(audio, axis=1)

    peak = np.max(np.abs(audio)) if len(audio) else 0
    if peak > 0:
        audio = audio / max(peak, 1.0)

    audio *= 0.3

    pitch_factor = 1.1
    audio = resample(audio, int(len(audio) / pitch_factor))

    audio = np.clip(audio, -1.0, 1.0)

    wav_buffer = io.BytesIO()
    sf.write(wav_buffer, audio, decoded.sample_rate, format="WAV")
    wav_buffer.seek(0)

    return StreamingResponse(wav_buffer, media_type="audio/wav")

@app.post("/memory/store")
def store(msg: MemoryMessage):
    memory.add_message(
        session_id=msg.session_id,
        role=msg.role,
        content=msg.content
    )

    return {"status": "saved"}

@app.post("/memory/query")
def search_memory(data: MemoryQuery):
    results = memory.search(
        query=data.query,
        session_id=data.session_id,
        top_k=data.top_k
    )

    return results