| 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 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"], |
| ) |
|
|
| 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) |
| |
| 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 |
|
|
| |
| 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" |
|
|
| |
| 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) |
|
|
| |
| 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) |
|
|
| |
| 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 |
| 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) |
|
|
| |
| 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_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 |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| def generate_map_markdown(data): |
| import numpy as np |
|
|
| |
| def create_room(room_char): |
| return [ |
| f"βββββ", |
| f"β {room_char} β", |
| f"βββββ" |
| ] |
|
|
| |
| rooms = [eval(room) for room in data["rooms"]] |
| rooms_of_interest = [eval(room) for room in data["room_of_interest"]] |
|
|
| |
| 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) |
|
|
| |
| map_height = (max_y - min_y + 1) * 3 |
| map_width = (max_x - min_x + 1) * 5 |
| grid = np.full((map_height, map_width), " ") |
|
|
| |
| 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) |
|
|
| |
| markdown_map = "\n".join("".join(row) for row in grid) |
|
|
| |
| return f"```\n{markdown_map}\n```" |