File size: 12,608 Bytes
b6b3361
fb3c56f
00866c1
257e92c
1ffdc41
4d96073
07631e1
 
 
 
4d96073
 
 
6234905
 
 
 
 
07631e1
235bceb
74a9029
619ea62
41ab4ee
97102e7
619ea62
4d96073
97102e7
 
4d96073
97102e7
257e92c
 
 
 
 
122cc50
257e92c
4d96073
257e92c
 
 
122cc50
257e92c
4d96073
257e92c
 
 
 
5bc8e83
4d96073
5bc8e83
122cc50
a309f78
4d96073
74a9029
4d96073
328e862
4d96073
 
b667879
4d96073
b667879
 
4d96073
07631e1
4d96073
 
6aa3952
4d96073
6aa3952
4d96073
 
 
 
826930b
4d96073
 
 
 
 
d8b00e3
4d96073
 
 
 
 
 
da84aa4
4d96073
07631e1
4d96073
07631e1
fea8f15
4d96073
 
 
 
 
e48e5e4
071af57
4d96073
071af57
fea8f15
4d96073
 
d8b00e3
4d96073
da84aa4
c4e11f2
4d96073
c4e11f2
4d96073
 
 
da84aa4
4d96073
 
 
 
da84aa4
74a9029
 
257e92c
a6818a7
257e92c
74a9029
4d96073
 
 
 
 
74a9029
4d96073
 
257e92c
da84aa4
be1fb13
48a69b6
07631e1
 
 
 
 
 
 
 
 
d8b00e3
4d96073
d8b00e3
 
 
 
 
4d96073
 
 
 
 
d8b00e3
 
 
 
 
07631e1
 
12bb931
07631e1
 
a309f78
da84aa4
6aa3952
e48e5e4
d8b00e3
4d96073
 
e48e5e4
12d43ff
e48e5e4
4d96073
071af57
4d96073
 
e48e5e4
 
d8b00e3
4d96073
 
826930b
 
 
4d96073
 
 
 
826930b
 
4d96073
 
 
f149c0d
 
d8b00e3
4d96073
 
 
 
d8b00e3
4d96073
5745bde
cf3d34c
9f5ffb5
4d96073
 
 
 
9f5ffb5
4d96073
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9f5ffb5
 
4d96073
6d4a898
cf3d34c
 
 
 
 
4d96073
 
4661cbb
781dafe
 
 
4661cbb
6b3b7ad
4661cbb
6b3b7ad
4661cbb
1d35c27
4661cbb
4d96073
 
6b3b7ad
41ab4ee
 
 
 
 
 
6b3b7ad
303d9ae
6b3b7ad
4d96073
 
 
 
 
 
 
 
 
33e23a9
4d96073
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33e23a9
4d96073
 
fb3c56f
f2fd3af
4d96073
5e07e0e
4d96073
5e07e0e
4d96073
781dafe
 
 
4d96073
 
781dafe
4d96073
d137ecb
 
4d96073
 
 
d8b00e3
4d96073
 
 
 
d8b00e3
4d96073
 
f149c0d
 
 
4d96073
f149c0d
 
 
 
 
 
 
4d96073
f149c0d
 
 
4d96073
f149c0d
 
 
 
 
4d96073
f149c0d
 
 
 
4d96073
f149c0d
 
 
 
 
 
 
 
 
 
 
 
 
 
4d96073
f149c0d
 
4d96073
f149c0d
 
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
import os
import time
from io import BytesIO
from langchain_core.pydantic_v1 import BaseModel, Field
from fastapi import FastAPI, HTTPException, Query, Request
from fastapi.responses import StreamingResponse, Response
from fastapi.middleware.cors import CORSMiddleware

from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from TextGen.suno import custom_generate_audio, get_audio_information, generate_lyrics
# from TextGen.diffusion import generate_image
# from coqui import predict
from langchain_google_genai import (
    ChatGoogleGenerativeAI,
    HarmBlockThreshold,
    HarmCategory,
)
from TextGen import app
from gradio_client import Client, handle_file
from typing import List
from elevenlabs.client import ElevenLabs
from elevenlabs import Voice, VoiceSettings, stream

Eleven_client = ElevenLabs(
    api_key=os.environ["ELEVEN_API_KEY"],  # Defaults to ELEVEN_API_KEY
)

Last_message = None

class PlayLastMusic(BaseModel):
    '''plays the lastest created music '''
    Desicion: str = Field(
        ..., description="Yes or No"
    )

class CreateLyrics(BaseModel):
    '''create some Lyrics for a new music'''
    Desicion: str = Field(
        ..., description="Yes or No"
    )

class CreateNewMusic(BaseModel):
    '''create a new music with the Lyrics previously computed'''
    Name: str = Field(
        ..., description="tags to describe the new music"
    )

class SongRequest(BaseModel):
    prompt: str | None = None
    tags: List[str] | None = None

class Message(BaseModel):
    npc: str | None = None
    messages: List[str] | None = None

class ImageGen(BaseModel):
    prompt: str | None = None

class VoiceMessage(BaseModel):
    npc: str | None = None
    input: str | None = None
    language: str | None = "en"
    genre: str | None = "Male"

song_base_api = os.environ["VERCEL_API"]
my_hf_token = os.environ["HF_TOKEN"]

tts_client = Client("Jofthomas/xtts", hf_token=my_hf_token)

main_npcs = {
    "Blacksmith": "./voices/Blacksmith.mp3",
    "Herbalist": "./voices/female.mp3",
    "Bard": "./voices/Bard_voice.mp3"
}

main_npcs_elevenlabs = {
    "Blacksmith": "yYdk7n49vTsUKiXxnosS",
    "Herbalist": "143zSsxc4O5ifS97lPCa",
    "Bard": "143zSsxc4O5ifS97lPCa"
}

main_npc_system_prompts = {
    "Blacksmith": "You are a blacksmith in a video game",
    "Herbalist": "You are an herbalist in a video game",
    "Witch": "You are a witch in a video game. You are disguised as a potion seller in a small city where adventurers come to challenge the portal. You are selling some magic spells in a UI that the player only sees. Don't event too much lore and just follow the standard role of a merchant.",
    "Bard": "You are a bard in a video game"
}

class Generate(BaseModel):
    text: str

class Rooms(BaseModel):
    rooms: List
    room_of_interest: List
    index_exit: int
    possible_entities: List
    logs: List

class Room_placements(BaseModel):
    placements: dict

class Invoke(BaseModel):
    system_prompt: str
    message: str

def generate_text(messages: List[str], npc: str):
    print(npc)
    if npc in main_npcs:
        system_prompt = main_npc_system_prompts[npc]
    else:
        system_prompt = "you're a character in a video game. Play along."
    print(system_prompt)
    new_messages = [{"role": "user", "content": system_prompt}]
    for index, message in enumerate(messages):
        if index % 2 == 0:
            new_messages.append({"role": "user", "content": message})
        else:
            new_messages.append({"role": "assistant", "content": message})
    print(new_messages)
    # Initialize the LLM
    llm = ChatGoogleGenerativeAI(
        model="gemini-1.5-pro-latest",
        max_output_tokens=100,
        temperature=1,
        safety_settings={
            HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
            HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
            HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
            HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE
        },
    )
    if npc == "bard":
        llm = llm.bind_tools([PlayLastMusic, CreateNewMusic, CreateLyrics])

    llm_response = llm.invoke(new_messages)
    print(llm_response)
    return Generate(text=llm_response.content)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

def inference_model(system_messsage, prompt):
    new_messages = [{"role": "user", "content": system_messsage}, {"role": "user", "content": prompt}]
    llm = ChatGoogleGenerativeAI(
        model="gemini-1.5-pro-latest",
        max_output_tokens=100,
        temperature=1,
        safety_settings={
            HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE,
            HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
            HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
            HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE
        },
    )
    llm_response = llm.invoke(new_messages)
    print(llm_response)
    return Generate(text=llm_response.content)

@app.get("/", tags=["Home"])
def api_home():
    return {'detail': 'Everchanging Quest backend, nothing to see here'}

@app.post("/api/generate", summary="Generate text from prompt", tags=["Generate"], response_model=Generate)
def inference(message: Message):
    return generate_text(messages=message.messages, npc=message.npc)

@app.post("/invoke_model")
def story(prompt: Invoke):
    return inference_model(system_messsage=prompt.system_prompt, prompt=prompt.message)

@app.post("/generate_level")
def placement(input: Rooms):
    print(input)
    markdown_map = generate_map_markdown(input)
    print(markdown_map)
    answer = {
        "key": "value"
    }
    return answer

# Dummy function for now
def determine_vocie_from_npc(npc, genre):
    if npc in main_npcs:
        return main_npcs[npc]
    else:
        if genre == "Male":
            return "./voices/default_male.mp3"
        if genre == "Female":
            return "./voices/default_female.mp3"
        else:
            return "./voices/narator_out.wav"

# Dummy function for now
def determine_elevenLav_voice_from_npc(npc, genre):
    if npc in main_npcs_elevenlabs:
        return main_npcs_elevenlabs[npc]
    else:
        if genre == "Male":
            return "bIHbv24MWmeRgasZH58o"
        if genre == "Female":
            return "pFZP5JQG7iQjIQuC4Bku"
        else:
            return "TX3LPaxmHKxFdv7VOQHJ"

@app.post("/generate_wav")
async def generate_wav(message: VoiceMessage):
    try:
        voice = determine_vocie_from_npc(message.npc, message.genre)
        audio_file_pth = handle_file(voice)

        # Generator function to yield audio chunks
        async def audio_stream():
            result = tts_client.predict(
                prompt=message.input,
                language=message.language,
                audio_file_pth=audio_file_pth,
                mic_file_path=None,
                use_mic=False,
                voice_cleanup=False,
                no_lang_auto_detect=False,
                agree=True,
                api_name="/predict"
            )
            for sampling_rate, audio_chunk in result:
                yield audio_chunk.tobytes()
                await asyncio.sleep(0)  # Yield control to the event loop

        # Return the generated audio as a streaming response
        return StreamingResponse(audio_stream(), media_type="audio/wav")

    except httpx.HTTPStatusError as e:
        if e.response.status_code == 403:
            raise HTTPException(status_code=403, detail="Access to the file is forbidden. Please check the file permissions and try again.")
        else:
            raise HTTPException(status_code=500, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/generate_voice_eleven", response_class=StreamingResponse)
@app.post("/generate_voice_eleven", response_class=StreamingResponse)
def generate_voice_eleven(message: VoiceMessage = None):
    global Last_message  # Declare Last_message as global
    if message is None:
        message = Last_message
    else:
        Last_message = message

    def audio_stream():
        this_voice_id = determine_elevenLav_voice_from_npc(message.npc, message.genre)

        # Generate the audio stream from ElevenLabs
        for chunk in Eleven_client.generate(text=message.input,
                                            voice=Voice(
                                                voice_id=this_voice_id,
                                                settings=VoiceSettings(stability=0.71, similarity_boost=0.5, style=0.0, use_speaker_boost=True)
                                            ),
                                            stream=True):
            yield chunk

    return StreamingResponse(audio_stream(), media_type="audio/mpeg")

# @app.get("/generate_voice_coqui", response_class=StreamingResponse)
# @app.post("/generate_voice_coqui", response_class=StreamingResponse)
# def generate_voice_coqui(message: VoiceMessage = None):
#     global Last_message
#     if message is None:
#         message = Last_message
#     else:
#         Last_message = message
#
#     def audio_stream():
#         voice = determine_vocie_from_npc(message.npc, message.genre)
#         result = predict(
#                 prompt=message.input,
#                 language=message.language,
#                 audio_file_pth=voice,
#                 mic_file_path=None,
#                 use_mic=False,
#                 voice_cleanup=False,
#                 no_lang_auto_detect=False,
#                 agree=True,
#             )
#         # Generate the audio stream from ElevenLabs
#         for chunk in result:
#             print("received : ",chunk)
#             yield chunk
#
#     return StreamingResponse(audio_stream(), media_type="audio/mpeg")

@app.get("/generate_song")
async def generate_song():
    text = """You are a bard in a video game singing the tales of a little girl in red hood."""

    song_lyrics = generate_lyrics({
        "prompt": f"{text}",
    })
    data = custom_generate_audio({
        "prompt": song_lyrics['text'],
        "tags": "male bard",
        "title": "Everchangin_Quest_song",
        "wait_audio": True,
    })
    infos = get_audio_information(f"{data[0]['id']},{data[1]['id']}")
    return infos

# @app.post('/generate_image')
# def Imagen(image: ImageGen = None):
#     pil_image = generate_image(image.prompt)
#
#     # Convert the PIL Image to bytes
#     img_byte_arr = BytesIO()
#     pil_image.save(img_byte_arr, format='PNG')
#     img_byte_arr = img_byte_arr.getvalue()
#
#     # Return the image as a PNG response
#     return Response(content=img_byte_arr, media_type="image/png")

def generate_map_markdown(data):
    import numpy as np

    # Define the room structure with walls and markers
    def create_room(room_char):
        return [
            f"╔═══╗",
            f"β•‘ {room_char} β•‘",
            f"β•šβ•β•β•β•"
        ]

    # Extract rooms and rooms of interest
    rooms = [eval(room) for room in data["rooms"]]
    rooms_of_interest = [eval(room) for room in data["room_of_interest"]]

    # Determine grid size
    min_x = min(room[0] for room in rooms)
    max_x = max(room[0] for room in rooms)
    min_y = min(room[1] for room in rooms)
    max_y = max(room[1] for room in rooms)

    # Create grid with empty spaces represented by a room-like structure
    map_height = (max_y - min_y + 1) * 3
    map_width = (max_x - min_x + 1) * 5
    grid = np.full((map_height, map_width), " ")

    # Populate grid with rooms and their characteristics
    for i, room in enumerate(rooms):
        x, y = room
        x_offset = (x - min_x) * 5
        y_offset = (max_y - y) * 3
        if room == (0, 0):
            room_char = "X"
        elif room in rooms_of_interest:
            room_char = "P" if i == data["index_exit"] else "?"
        else:
            room_char = " "
        room_structure = create_room(room_char)
        for j, row in enumerate(room_structure):
            grid[y_offset + j, x_offset:x_offset + 5] = list(row)

    # Convert grid to a string format suitable for display
    markdown_map = "\n".join("".join(row) for row in grid)

    # Return the map wrapped in triple backticks for proper display in markdown
    return f"```\n{markdown_map}\n```"