Mbonea commited on
Commit
c4ed5be
·
1 Parent(s): fc39edf

character ai

Browse files
App/TTS/Schemas.py CHANGED
@@ -30,6 +30,12 @@ class DescriptTranscript(BaseModel):
30
  file_extenstion: str = ".wav"
31
 
32
 
 
 
 
 
 
 
33
  class PiTTSRequest(BaseModel):
34
  text: str
35
  voice: Optional[str]
 
30
  file_extenstion: str = ".wav"
31
 
32
 
33
+ # Define the request model
34
+ class CharacterTTSRequest(BaseModel):
35
+ text: str
36
+ voice: Optional[str] = None
37
+
38
+
39
  class PiTTSRequest(BaseModel):
40
  text: str
41
  voice: Optional[str]
App/TTS/TTSRoutes.py CHANGED
@@ -10,16 +10,18 @@ from .Schemas import (
10
  DescriptSfxRequest,
11
  DescriptTranscript,
12
  PiTTSRequest,
 
13
  )
14
  from .utils.Podcastle import PodcastleAPI
15
  from .utils.HeyGen import HeygenAPI
16
-
17
  from .utils.Descript import DescriptTTS
18
  import os
19
  import asyncio
20
- from .utils.Pi import PiAIClient
21
 
22
- from fastapi import FastAPI, Request, HTTPException
 
 
23
  from fastapi.responses import StreamingResponse, FileResponse
24
  import os
25
  import aiofiles
@@ -35,7 +37,7 @@ data = {
35
 
36
  descript_tts = DescriptTTS()
37
  heyGentts = HeygenAPI(**data)
38
- pi = PiAIClient(headless=True)
39
 
40
 
41
  @tts_router.post("/generate_tts")
@@ -97,11 +99,17 @@ async def search_id(req: StatusRequest):
97
  return await tts.check_status(req)
98
 
99
 
100
- @tts_router.post("/pi_tts")
101
- async def pi_tts(req: PiTTSRequest):
102
- if not pi.initialized:
103
- await pi.setup()
104
- return await pi.say(req.text, voice=req.voice)
 
 
 
 
 
 
105
 
106
 
107
  @tts_router.get("/audio/{audio_name}")
 
10
  DescriptSfxRequest,
11
  DescriptTranscript,
12
  PiTTSRequest,
13
+ CharacterTTSRequest,
14
  )
15
  from .utils.Podcastle import PodcastleAPI
16
  from .utils.HeyGen import HeygenAPI
17
+ from .utils.CharacterAi import CharacterTTS
18
  from .utils.Descript import DescriptTTS
19
  import os
20
  import asyncio
 
21
 
22
+ # from .utils.Pi import PiAIClient
23
+
24
+ from fastapi import Request, HTTPException
25
  from fastapi.responses import StreamingResponse, FileResponse
26
  import os
27
  import aiofiles
 
37
 
38
  descript_tts = DescriptTTS()
39
  heyGentts = HeygenAPI(**data)
40
+ # pi = PiAIClient(headless=True)
41
 
42
 
43
  @tts_router.post("/generate_tts")
 
99
  return await tts.check_status(req)
100
 
101
 
102
+ @tts_router.post("/cai_tts")
103
+ async def cai_tts(req: CharacterTTSRequest):
104
+ cai = CharacterTTS()
105
+ return await cai.say(req)
106
+
107
+
108
+ # @tts_router.post("/pi_tts")
109
+ # async def pi_tts(req: PiTTSRequest):
110
+ # if not pi.initialized:
111
+ # await pi.setup()
112
+ # return await pi.say(req.text, voice=req.voice)
113
 
114
 
115
  @tts_router.get("/audio/{audio_name}")
App/TTS/utils/CharacterAi.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import os
3
+ from characterai import aiocai
4
+ from typing import Optional
5
+ from pydantic import BaseModel
6
+ import aiohttp
7
+
8
+ from App.TTS.Schemas import CharacterTTSRequest
9
+
10
+
11
+ # class CharacterTTSRequest(BaseModel):
12
+ # text: str
13
+ # voice: Optional[str] = None
14
+
15
+
16
+ # Voice ID selected by the user
17
+
18
+
19
+ class CharacterTTS:
20
+ TTS_ENDPOINT = "https://neo.character.ai/multimodal/api/v1/memo/replay"
21
+
22
+ def __init__(self):
23
+ """
24
+ Initialize the CharacterTTS instance.
25
+
26
+ Args:
27
+ token (str): The authorization token for Character AI.
28
+ """
29
+ self.token = "110626c85c57978218fa0066f58da72807e179d2"
30
+ self.client = aiocai.Client(self.token)
31
+
32
+ async def say(
33
+ self,
34
+ request: CharacterTTSRequest,
35
+ char_id: str = "nrXc-uQ5vtunnBTqKxB39vwNyJg2TARqD9r2wHVRO4U",
36
+ ) -> str:
37
+ """
38
+ Send a message to the character AI and generate TTS.
39
+
40
+ Args:
41
+ char_id (str): The character ID to chat with.
42
+ request (CharacterTTSRequest): The TTS request containing text and optional voice ID.
43
+
44
+ Returns:
45
+ str: The replay URL for the generated TTS audio.
46
+ """
47
+ # Connect to the AI client and start a new chat
48
+ me = await self.client.get_me()
49
+ chat = await self.client.connect()
50
+ async with chat as chat_session:
51
+ new_chat, answer = await chat_session.new_chat(char_id, me.id)
52
+ chat_id = new_chat.chat_id
53
+
54
+ # Send the user's message
55
+ message = await chat_session.send_message(
56
+ char=char_id, chat_id=chat_id, text=request.text
57
+ )
58
+
59
+ # Extract necessary identifiers
60
+ room_id = chat_id
61
+ turn_id = message.turn_key.turn_id
62
+ primary_candidate = next(
63
+ (
64
+ c
65
+ for c in message.candidates
66
+ if c.candidate_id == message.primary_candidate_id
67
+ ),
68
+ None,
69
+ )
70
+
71
+ if not primary_candidate:
72
+ raise Exception("Primary candidate not found in the message response.")
73
+
74
+ candidate_id = primary_candidate.candidate_id
75
+ voice_id = request.voice if request.voice else self.default_voice()
76
+
77
+ # Generate TTS and get the replay URL
78
+ replay_url = await self.generate_tts(
79
+ room_id, turn_id, candidate_id, voice_id
80
+ )
81
+
82
+ return replay_url
83
+
84
+ def default_voice(self) -> str:
85
+ """
86
+ Return a default voice ID if none is provided.
87
+
88
+ Returns:
89
+ str: The default voice ID.
90
+ """
91
+ return (
92
+ "0ef6c2d5-14e1-420c-a634-693c3f13fade" # Replace with your default voice ID
93
+ )
94
+
95
+ async def generate_tts(
96
+ self, room_id: str, turn_id: str, candidate_id: str, voice_id: str
97
+ ) -> str:
98
+ """
99
+ Generate TTS by making a POST request to the TTS endpoint.
100
+
101
+ Args:
102
+ room_id (str): The chat room ID.
103
+ turn_id (str): The turn ID of the message.
104
+ candidate_id (str): The candidate ID of the message.
105
+ voice_id (str): The selected voice ID.
106
+
107
+ Returns:
108
+ str: The replay URL for the generated TTS audio.
109
+ """
110
+ payload = {
111
+ "roomId": room_id,
112
+ "turnId": turn_id,
113
+ "candidateId": candidate_id,
114
+ "voiceId": voice_id,
115
+ }
116
+
117
+ headers = {
118
+ "accept": "application/json, text/plain, */*",
119
+ "accept-language": "en-US,en;q=0.9",
120
+ "authorization": f"Token {self.token}",
121
+ "content-type": "application/json",
122
+ "origin": "https://character.ai",
123
+ "priority": "u=1, i",
124
+ "referer": "https://character.ai/",
125
+ "sec-ch-ua": '"Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"',
126
+ "sec-ch-ua-mobile": "?0",
127
+ "sec-ch-ua-platform": '"Windows"',
128
+ "sec-fetch-dest": "empty",
129
+ "sec-fetch-mode": "cors",
130
+ "sec-fetch-site": "same-site",
131
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
132
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
133
+ "Chrome/129.0.0.0 Safari/537.36",
134
+ }
135
+
136
+ async with aiohttp.ClientSession() as session:
137
+ async with session.post(
138
+ self.TTS_ENDPOINT, json=payload, headers=headers
139
+ ) as response:
140
+ if response.status == 200:
141
+ data = await response.json()
142
+ replay_url = data.get("replayUrl")
143
+ if replay_url:
144
+ print(f"TTS generated successfully. Replay URL: {replay_url}")
145
+ return replay_url
146
+ else:
147
+ raise Exception("Replay URL not found in the response.")
148
+ else:
149
+ error = await response.text()
150
+ raise Exception(
151
+ f"TTS generation failed with status {response.status}: {error}"
152
+ )
153
+
154
+
155
+ # async def main():
156
+ # cai = CharacterTTS()
157
+ # x = await cai.say(CharacterTTSRequest(text="Hello, how are you?"))
158
+ # print(x)
159
+
160
+
161
+ # if __name__ == "__main__":
162
+ # asyncio.run(main())
Dockerfile CHANGED
@@ -28,7 +28,6 @@ RUN apt-get update && \
28
  #copy requirements
29
  COPY requirements.txt .
30
  RUN pip install --no-cache-dir -r requirements.txt
31
- RUN playwright install-deps
32
 
33
 
34
  # Copy the application code
@@ -37,6 +36,6 @@ USER admin
37
  COPY --chown=admin . /srv
38
 
39
  # Command to run the application
40
- CMD playwright install && uvicorn App.app:app --host 0.0.0.0 --port 7860 --workers 1
41
  # Expose the server port
42
  EXPOSE 7860
 
28
  #copy requirements
29
  COPY requirements.txt .
30
  RUN pip install --no-cache-dir -r requirements.txt
 
31
 
32
 
33
  # Copy the application code
 
36
  COPY --chown=admin . /srv
37
 
38
  # Command to run the application
39
+ CMD uvicorn App.app:app --host 0.0.0.0 --port 7860 --workers 1
40
  # Expose the server port
41
  EXPOSE 7860
requirements.txt CHANGED
@@ -30,7 +30,7 @@ glfw
30
  Pillow
31
  numpy
32
  broken-source
33
- playwright
34
 
35
  # git+https://github.com/snowby666/poe-api-wrapper.git@2c91207598ad930901cb9fb09734705056b7b6a9
36
  # git+https://github.com/Mbonea-Mjema/ballyregan.git
 
30
  Pillow
31
  numpy
32
  broken-source
33
+ git+https://github.com/Xtr4F/PyCharacterAI.git
34
 
35
  # git+https://github.com/snowby666/poe-api-wrapper.git@2c91207598ad930901cb9fb09734705056b7b6a9
36
  # git+https://github.com/Mbonea-Mjema/ballyregan.git