Use this code to login:
110 |{{ .Token }}
111 | ``` 112 | 113 | ## Credits 114 | 115 | Thanks to [nasan16](https://github.com/nasan016) for their initial work on 116 | [webshell](https://github.com/nasan016/webshell) and [Andy 117 | Ayrey](https://x.com/AndyAyrey) for his work on [Infinite 118 | Backrooms](https://dreams-of-an-electric-mind.webflow.io/), whose prompts 119 | heavily inspired this project. 120 | -------------------------------------------------------------------------------- /api/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | from contextvars import ContextVar 3 | from typing import Annotated, Any, Dict, Optional 4 | from functools import cache 5 | from fastapi.security import OAuth2PasswordBearer 6 | from fastapi.responses import FileResponse 7 | 8 | import sentry_sdk 9 | from fastapi import Depends, FastAPI, HTTPException, status 10 | from starlette.middleware.cors import CORSMiddleware 11 | from fastapi.responses import StreamingResponse 12 | from honcho import Honcho, NotGiven 13 | 14 | from cryptography.fernet import Fernet 15 | import base64 16 | 17 | from calls import GaslitClaude, Simulator, Constructor, Summary, Identity 18 | import models 19 | import jwt 20 | 21 | from dotenv import load_dotenv 22 | 23 | import tempfile 24 | import json 25 | from datetime import datetime 26 | 27 | load_dotenv(override=True) 28 | 29 | 30 | def get_env(key: str): 31 | var = os.getenv(key) 32 | if not var: 33 | raise ValueError(f"{key} is not set in .env") 34 | return var 35 | 36 | 37 | HONCHO_ENV = get_env("HONCHO_ENV") 38 | CLIENT_REGEX = get_env("CLIENT_REGEX") 39 | print(CLIENT_REGEX) 40 | JWT_SECRET = get_env("JWT_SECRET") 41 | # SECRET_KEY = get_env("SECRET_KEY").encode() 42 | SECRET_KEY = base64.b64decode(get_env("SECRET_KEY")) 43 | HONCHO_APP_NAME = get_env("HONCHO_APP_NAME") 44 | HONCHO_SUMMARY_METAMESSAGE_TYPE = "constructor_summary" 45 | 46 | fernet = Fernet(SECRET_KEY) 47 | 48 | print(f"Initializing Honcho with base_url: {HONCHO_ENV}") 49 | honcho = Honcho( 50 | base_url=HONCHO_ENV, 51 | ) 52 | 53 | try: 54 | print(f"Attempting to get/create app: {HONCHO_APP_NAME}") 55 | honcho_app = honcho.apps.get_or_create(HONCHO_APP_NAME) 56 | print(f"Successfully initialized app with id: {honcho_app.id}") 57 | except Exception as e: 58 | print(f"Error initializing Honcho app: {str(e)}") 59 | raise 60 | 61 | 62 | gaslit_ctx = ContextVar( 63 | "gaslit_claude", default=GaslitClaude(name="", insights="", history=[]) 64 | ) 65 | simulator_ctx = ContextVar("simulator", default=Simulator(history=[], name="")) 66 | constructor_ctx = ContextVar("constructor", default=Constructor(history=[])) 67 | summary_ctx = ContextVar("summary", default=Summary(history=[])) 68 | identity_ctx = ContextVar[Optional[Identity]]("identity", default=None) 69 | 70 | 71 | sentry_sdk.init( 72 | dsn=os.getenv("SENTRY_DSN"), 73 | traces_sample_rate=0.3, 74 | profiles_sample_rate=0.3, 75 | ) 76 | 77 | app = FastAPI() 78 | 79 | app.add_middleware( 80 | CORSMiddleware, 81 | allow_origin_regex=os.getenv("CLIENT_REGEX"), 82 | allow_origins=["*"], 83 | allow_credentials=True, 84 | allow_methods=["*"], 85 | allow_headers=["*"], 86 | ) 87 | 88 | oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") 89 | 90 | ################# 91 | # Utility functions 92 | ################# 93 | 94 | 95 | @cache 96 | async def get_or_create_user_from_name(user_id: str): 97 | user = honcho.apps.users.get_or_create(app_id=honcho_app.id, name=user_id) 98 | return user 99 | 100 | 101 | async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): 102 | credentials_exception = HTTPException( 103 | status_code=status.HTTP_401_UNAUTHORIZED, 104 | detail="Could not validate credentials", 105 | headers={"WWW-Authenticate": "Bearer"}, 106 | ) 107 | 108 | try: 109 | payload = jwt.decode( 110 | token, 111 | JWT_SECRET, 112 | algorithms=["HS256"], 113 | audience="authenticated", 114 | ) 115 | user = honcho.apps.users.get_or_create( 116 | app_id=honcho_app.id, name=payload["sub"] 117 | ) 118 | return user.id 119 | except jwt.InvalidTokenError as e: 120 | print(e) 121 | raise credentials_exception 122 | 123 | 124 | @app.get("/user") 125 | async def user(name: str): 126 | user = honcho.apps.users.get_or_create(app_id=honcho_app.id, name=name) 127 | return { 128 | "user_id": user.id, 129 | } 130 | 131 | 132 | def messages(res: models.BaseRequest, user_id: str): 133 | gaslit_claude = GaslitClaude(name="", insights="", history=[]) 134 | simulator = Simulator(history=[], name="") 135 | history_iter = honcho.apps.users.sessions.messages.list( 136 | app_id=honcho_app.id, session_id=res.session_id, user_id=user_id 137 | ) 138 | 139 | gaslit_claude.history = [] 140 | simulator.history = [] 141 | 142 | for message in history_iter: 143 | if message.is_user: 144 | gaslit_claude.history += [{"role": "assistant", "content": message.content}] 145 | simulator.history += [{"role": "user", "content": message.content}] 146 | else: 147 | gaslit_claude.history += [{"role": "user", "content": message.content}] 148 | simulator.history += [{"role": "assistant", "content": message.content}] 149 | 150 | gaslit_ctx.set(gaslit_claude) 151 | simulator_ctx.set(simulator) 152 | 153 | 154 | ################ 155 | # Simulation Function 156 | ################ 157 | 158 | 159 | def manual_turn(res: models.ManualRequest, user_id: str): 160 | gaslit_response = res.command 161 | simulator_response = "" 162 | simulator = simulator_ctx.get() 163 | simulator.history += [{"role": "user", "content": res.command}] # type: ignore 164 | response = simulator.stream() 165 | for text in response: 166 | simulator_response += text 167 | yield text 168 | 169 | honcho.apps.users.sessions.messages.create( 170 | session_id=res.session_id, 171 | app_id=honcho_app.id, 172 | user_id=user_id, 173 | content=gaslit_response, 174 | is_user=True, 175 | ) 176 | honcho.apps.users.sessions.messages.create( 177 | session_id=res.session_id, 178 | app_id=honcho_app.id, 179 | user_id=user_id, 180 | content=simulator_response, 181 | is_user=False, 182 | ) 183 | 184 | 185 | @app.post("/manual") 186 | async def manual(res: models.ManualRequest, user_id: str = Depends(get_current_user)): 187 | messages(res, user_id) 188 | return StreamingResponse(manual_turn(res, user_id)) 189 | 190 | 191 | @app.post("/auto") 192 | async def auto(res: models.BaseRequest, user_id: str = Depends(get_current_user)): 193 | messages(res, user_id) 194 | 195 | def convo(): 196 | gaslit_response = "" 197 | gaslit_claude = gaslit_ctx.get() 198 | response = gaslit_claude.stream() 199 | for text in response: 200 | gaslit_response += text 201 | yield text 202 | 203 | return StreamingResponse(convo()) 204 | 205 | 206 | ################ 207 | # Constructor Functions 208 | ################ 209 | 210 | 211 | # This is very similar to the manual turn. Maybe if we pass an argument 212 | # indicating the type of turn we can reuse the same function and get either 213 | # the constructor or the simulator from the context 214 | def constructor_messages(res: models.ManualRequest, user_id: str): 215 | constructor = constructor_ctx.get() 216 | summary = summary_ctx.get() 217 | history_iter = honcho.apps.users.sessions.messages.list( 218 | app_id=honcho_app.id, session_id=res.session_id, user_id=user_id 219 | ) 220 | constructor.history = [] 221 | summary.history = [] 222 | for message in history_iter: 223 | if message.is_user: 224 | constructor.history += [{"role": "user", "content": message.content}] 225 | summary.history += [{"role": "user", "content": message.content}] 226 | else: 227 | constructor.history += [{"role": "assistant", "content": message.content}] 228 | summary.history += [{"role": "assistant", "content": message.content}] 229 | print(f"in constructor_messages, constructor.history: {constructor.history}") 230 | print(f"in constructor_messages, summary.history: {summary.history}") 231 | constructor_ctx.set(constructor) 232 | summary_ctx.set(summary) 233 | 234 | 235 | def constructor_turn(res: models.ManualRequest, user_id: str): 236 | user_message = res.command 237 | constructor_response = "" 238 | constructor = constructor_ctx.get() 239 | constructor.history += [{"role": "user", "content": user_message}] # type: ignore 240 | response = constructor.stream() 241 | for text in response: 242 | constructor_response += text 243 | yield text 244 | 245 | user_honcho_message = honcho.apps.users.sessions.messages.create( 246 | session_id=res.session_id, 247 | app_id=honcho_app.id, 248 | user_id=user_id, 249 | content=user_message, 250 | is_user=True, 251 | ) 252 | honcho.apps.users.sessions.messages.create( 253 | session_id=res.session_id, 254 | app_id=honcho_app.id, 255 | user_id=user_id, 256 | content=constructor_response, 257 | is_user=False, 258 | ) 259 | summary = summary_turn(res, user_id) 260 | metamessage = honcho.apps.users.sessions.metamessages.create( 261 | session_id=res.session_id, 262 | app_id=honcho_app.id, 263 | user_id=user_id, 264 | content=summary, 265 | message_id=user_honcho_message.id, 266 | metamessage_type=HONCHO_SUMMARY_METAMESSAGE_TYPE, 267 | ) 268 | print(f"metamessage: {metamessage}") 269 | 270 | 271 | @app.post("/constructor") 272 | async def constructor( 273 | res: models.ManualRequest, user_id: str = Depends(get_current_user) 274 | ): 275 | constructor_messages(res, user_id) 276 | return StreamingResponse(constructor_turn(res, user_id)) 277 | 278 | 279 | def summary_turn(res: models.ManualRequest, user_id: str): 280 | summary = summary_ctx.get() 281 | summary.history += [{"role": "user", "content": res.command}] # type: ignore 282 | response = summary.stream() 283 | summary = "" 284 | for text in response: 285 | summary += text 286 | return summary 287 | 288 | 289 | @app.post("/constructor/summary") 290 | async def constructor_summary( 291 | res: models.ManualRequest, user_id: str = Depends(get_current_user) 292 | ): 293 | constructor_messages(res, user_id) 294 | return StreamingResponse(summary_turn(res, user_id)) 295 | 296 | 297 | @app.get("/summary") 298 | async def summary(session_id: str, user_id: str = Depends(get_current_user)): 299 | metamessage_iter = honcho.apps.users.sessions.metamessages.list( 300 | session_id=session_id, 301 | app_id=honcho_app.id, 302 | user_id=user_id, 303 | metamessage_type=HONCHO_SUMMARY_METAMESSAGE_TYPE, 304 | ) 305 | return [metamessage for metamessage in metamessage_iter] 306 | 307 | 308 | @app.get("/identity") 309 | async def identity( message_id: str, metamessage_id: str, session_id: str, user_id: str = Depends(get_current_user),): 310 | print("session_id", session_id) 311 | print("user_id", user_id) 312 | print("message_id", message_id) 313 | print("metamessage_id", metamessage_id) 314 | metamessage = honcho.apps.users.sessions.metamessages.get( 315 | session_id=session_id, 316 | app_id=honcho_app.id, 317 | user_id=user_id, 318 | message_id=message_id, 319 | metamessage_id=metamessage_id, 320 | ) 321 | identity = Identity(metamessage.content, "") 322 | messages = identity._get_identity() 323 | return messages 324 | 325 | @app.post("/reset") 326 | async def reset( 327 | session_id: str | None = None, 328 | mode: str | None = "simulator", 329 | user_id: str = Depends(get_current_user), 330 | ): 331 | if session_id: 332 | honcho.apps.users.sessions.delete( 333 | app_id=honcho_app.id, session_id=session_id, user_id=user_id 334 | ) 335 | # TODO reset the session 336 | # gaslit_claude.history = [] 337 | # simulator.history = [] 338 | metadata = {} 339 | if mode == "constructor": 340 | metadata["mode"] = "constructor" 341 | try: 342 | session = honcho.apps.users.sessions.create( 343 | app_id=honcho_app.id, 344 | user_id=user_id, 345 | metadata=metadata, 346 | ) 347 | except TypeError as e: 348 | if "location_id" in str(e): 349 | # If location_id is truly optional, try without it 350 | session = honcho.apps.users.sessions.create( 351 | app_id=honcho_app.id, user_id=user_id 352 | ) 353 | else: 354 | raise e 355 | 356 | return { 357 | "user_id": user_id, 358 | "session_id": session.id, 359 | } 360 | 361 | 362 | @app.get("/session") 363 | async def get_session_messages( 364 | session_id: str | None = None, user_id: str = Depends(get_current_user) 365 | ): 366 | resolved_session_id: str 367 | if not session_id: 368 | # Fetch the latest session if session_id is not provided 369 | sessions = honcho.apps.users.sessions.list( 370 | app_id=honcho_app.id, user_id=user_id, size=1, reverse=True 371 | ) 372 | sessions_list = list(sessions) 373 | if not sessions_list: 374 | raise HTTPException(status_code=404, detail="No sessions found") 375 | latest_session = sessions_list[0] 376 | resolved_session_id = str(latest_session.id) 377 | else: 378 | resolved_session_id = session_id 379 | 380 | try: 381 | # Fetch messages for the given or latest session 382 | messages = honcho.apps.users.sessions.messages.list( 383 | app_id=honcho_app.id, user_id=user_id, session_id=resolved_session_id 384 | ) 385 | return { 386 | "session_id": resolved_session_id, 387 | "messages": [ 388 | { 389 | "id": msg.id, 390 | "content": msg.content, 391 | "created_at": msg.created_at, 392 | "is_user": msg.is_user, 393 | } 394 | for msg in messages 395 | ], 396 | } 397 | except Exception as e: 398 | return {"error": f"Failed to fetch messages: {str(e)}"} 399 | 400 | 401 | @app.get("/sessions") 402 | async def get_sessions( 403 | mode: str = "simulator", user_id: str = Depends(get_current_user) 404 | ): 405 | try: 406 | filter_dict: Dict[str, Any] = {"mode": mode} 407 | sessions = honcho.apps.users.sessions.list( 408 | app_id=honcho_app.id, 409 | user_id=user_id, 410 | reverse=True, # Get the most recent sessions first 411 | filter=filter_dict, 412 | ) 413 | return [session for session in sessions] 414 | except Exception as e: 415 | return {"error": f"Failed to fetch sessions: {str(e)}"} 416 | 417 | 418 | @app.put("/sessions/{session_id}/metadata") 419 | async def update_session_metadata( 420 | session_id: str, metadata: Dict[str, Any], user_id: str = Depends(get_current_user) 421 | ): 422 | try: 423 | updated_session = honcho.apps.users.sessions.update( 424 | session_id=session_id, 425 | app_id=honcho_app.id, 426 | user_id=user_id, 427 | metadata=metadata, 428 | ) 429 | return {"session_id": updated_session.id, "metadata": updated_session.metadata} 430 | except Exception as e: 431 | raise HTTPException( 432 | status_code=400, detail=f"Failed to update session metadata: {str(e)}" 433 | ) 434 | 435 | 436 | ################## 437 | # Share and Export Functions 438 | ################## 439 | 440 | 441 | @app.get("/share/{session_id}") 442 | async def share(session_id: str, user_id: str = Depends(get_current_user)): 443 | # return encrypted session_id and user_id 444 | encrypted = fernet.encrypt(f"{session_id}:{user_id}".encode()) 445 | return {"code": encrypted.decode()} 446 | 447 | 448 | async def resolve_legacy_user_id(legacy_user_id: str) -> str: 449 | try: 450 | users = honcho.apps.users.list(app_id=honcho_app.id) 451 | for user in users: 452 | if user.metadata and user.metadata.get("legacy_id") == legacy_user_id: 453 | return user.id 454 | raise HTTPException( 455 | status_code=404, detail=f"User not found with legacy ID {legacy_user_id}" 456 | ) 457 | except Exception as e: 458 | raise HTTPException(status_code=404, detail=f"Failed to resolve user: {str(e)}") 459 | 460 | 461 | def is_legacy_id(id_str: str) -> bool: 462 | """ 463 | Determine if an ID is a legacy UUID based on its format. 464 | Legacy UUIDs are 36 characters long with hyphens (e.g., 550e8400-e29b-41d4-a716-446655440000) 465 | New IDs are shorter nanoid strings 466 | """ 467 | return len(id_str) == 36 and id_str.count("-") == 4 468 | 469 | 470 | async def resolve_legacy_session_id(session_id: str, user_id: str) -> str: 471 | """ 472 | Only used for resolving potentially legacy session IDs from share URLs. 473 | Returns the current valid session ID. 474 | """ 475 | try: 476 | sessions_page = honcho.apps.users.sessions.list( 477 | app_id=honcho_app.id, user_id=user_id 478 | ) 479 | sessions = list(sessions_page) 480 | 481 | # Look for a session with matching id 482 | for session in sessions: 483 | if str(session.id) == session_id: 484 | return str(session.id) 485 | 486 | print("No session found with matching ID, checking legacy metadata...") 487 | # If not found, check legacy metadata 488 | for session in sessions: 489 | if session.metadata and session.metadata.get("legacy_id") == session_id: 490 | print(f"Found matching legacy session! ID: {session.id}") 491 | return str(session.id) 492 | 493 | raise HTTPException( 494 | status_code=404, detail=f"Session not found with ID {session_id}" 495 | ) 496 | except Exception as e: 497 | print(f"Failed to resolve session: {str(e)}") 498 | print(f"Exception type: {type(e)}") 499 | raise HTTPException( 500 | status_code=404, detail=f"Failed to resolve session: {str(e)}" 501 | ) 502 | 503 | 504 | @app.get("/share/messages/{code}") 505 | async def get_shared_messages(code: str): 506 | try: 507 | decrypted = fernet.decrypt(code.encode()).decode() 508 | session_id, user_id = decrypted.split(":") 509 | 510 | if is_legacy_id(user_id): 511 | current_user_id = await resolve_legacy_user_id(user_id) 512 | sessions = honcho.apps.users.sessions.list( 513 | app_id=honcho_app.id, user_id=current_user_id 514 | ) 515 | 516 | for session in sessions: 517 | if session.metadata and session.metadata.get("legacy_id") == session_id: 518 | return await get_session_messages( 519 | session_id=str(session.id), user_id=current_user_id 520 | ) 521 | 522 | raise HTTPException( 523 | status_code=404, detail=f"Legacy session not found with ID {session_id}" 524 | ) 525 | 526 | # For non-legacy IDs, try direct matching 527 | else: 528 | try: 529 | return await get_session_messages( 530 | session_id=session_id, user_id=user_id 531 | ) 532 | except Exception: 533 | raise HTTPException( 534 | status_code=404, detail=f"Session not found with ID {session_id}" 535 | ) 536 | 537 | except Exception as e: 538 | raise HTTPException( 539 | status_code=400, detail=f"Invalid share code or session not found: {str(e)}" 540 | ) 541 | 542 | 543 | @app.get("/export/{session_id}") 544 | async def export_session(session_id: str, user_id: str = Depends(get_current_user)): 545 | try: 546 | messages = honcho.apps.users.sessions.messages.list( 547 | app_id=honcho_app.id, user_id=user_id, session_id=session_id 548 | ) 549 | 550 | formatted_messages = [ 551 | {"role": "user" if msg.is_user else "assistant", "content": msg.content} 552 | for msg in messages 553 | ] 554 | 555 | # Create a temporary file 556 | with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".json") as tmp: 557 | json.dump(formatted_messages, tmp, indent=2) 558 | tmp_path = tmp.name 559 | 560 | timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") 561 | filename = f"yousim_conversation_{timestamp}.json" 562 | 563 | return FileResponse( 564 | path=tmp_path, 565 | filename=filename, 566 | media_type="application/json", 567 | background=None, # Ensures file is sent before deletion 568 | ) 569 | 570 | except Exception as e: 571 | raise HTTPException( 572 | status_code=400, detail=f"Failed to export session: {str(e)}" 573 | ) 574 | 575 | 576 | class ChatRequest(models.BaseRequest): 577 | command: str 578 | original_session_id: str 579 | summary_id: str 580 | summary_message_id: str 581 | session_id: str 582 | prompt: Optional[list[dict]] = None 583 | 584 | async def chat_turn(res: ChatRequest, user_id: str): 585 | try: 586 | metamessage = honcho.apps.users.sessions.metamessages.get( 587 | session_id=res.original_session_id, 588 | app_id=honcho_app.id, 589 | user_id=user_id, 590 | metamessage_id=res.summary_id, 591 | message_id=res.summary_message_id, 592 | ) 593 | 594 | print("Summary Message", metamessage) 595 | 596 | if not metamessage: 597 | raise HTTPException(status_code=404, detail="Summary not found") 598 | 599 | # Get session metadata to find the summary_id 600 | session = honcho.apps.users.sessions.get( 601 | session_id=res.session_id, 602 | app_id=honcho_app.id, 603 | user_id=user_id, 604 | ) 605 | print("Session", session) 606 | 607 | if not session: 608 | raise HTTPException(status_code=400, detail="Invalid chat session") 609 | 610 | # Get chat history 611 | history = [] 612 | messages = honcho.apps.users.sessions.messages.list( 613 | app_id=honcho_app.id, 614 | user_id=user_id, 615 | session_id=res.session_id, 616 | ) 617 | for message in messages: 618 | history.append({ 619 | "role": "user" if message.is_user else "assistant", 620 | "content": message.content 621 | }) 622 | 623 | # print("History", history) 624 | print("Prompt", res.prompt) 625 | 626 | 627 | # Create and store Identity instance in context with history 628 | identity = Identity(metamessage.content, res.command, res.prompt) 629 | identity.history = history 630 | identity_ctx.set(identity) 631 | 632 | # Get response from Identity 633 | response = identity.stream() 634 | print("Response", response) 635 | 636 | response_text = "" 637 | for text in response: 638 | print(text) 639 | response_text += text 640 | yield text 641 | 642 | # Store messages in session 643 | honcho.apps.users.sessions.messages.create( 644 | session_id=res.session_id, 645 | app_id=honcho_app.id, 646 | user_id=user_id, 647 | content=res.command, 648 | is_user=True, 649 | ) 650 | honcho.apps.users.sessions.messages.create( 651 | session_id=res.session_id, 652 | app_id=honcho_app.id, 653 | user_id=user_id, 654 | content=response_text, 655 | is_user=False, 656 | ) 657 | except Exception as e: 658 | raise HTTPException( 659 | status_code=500, 660 | detail=f"Failed to process chat request: {str(e)}" 661 | ) 662 | 663 | @app.post("/chat") 664 | async def chat(res: ChatRequest, user_id: str = Depends(get_current_user)): 665 | print("Get Chat") 666 | return StreamingResponse(chat_turn(res, user_id)) 667 | -------------------------------------------------------------------------------- /api/calls.py: -------------------------------------------------------------------------------- 1 | from os import getenv 2 | from dotenv import load_dotenv 3 | from anthropic import Anthropic 4 | from openai import OpenAI 5 | from groq import Groq 6 | from typing import Optional 7 | 8 | from functools import cache 9 | import re 10 | 11 | load_dotenv(override=True) 12 | 13 | anthropic = Anthropic( 14 | api_key=getenv("ANTHROPIC_API_KEY", "placeholder"), 15 | ) 16 | openai = OpenAI( 17 | base_url="https://openrouter.ai/api/v1", 18 | api_key=getenv("OPENAI_API_KEY", "placeholder"), 19 | ) 20 | 21 | groq = Groq( 22 | api_key=getenv("GROQ_API_KEY"), 23 | ) 24 | 25 | PROVIDER = getenv("PROVIDER") 26 | 27 | 28 | def completion_handler(res, provider: str): 29 | if provider == "anthropic": 30 | with res as stream: 31 | for text in stream.text_stream: 32 | yield text 33 | else: 34 | for chunk in res: 35 | yield chunk.choices[0].delta.content or "" 36 | 37 | 38 | class GaslitClaude: 39 | def __init__(self, name: str, insights: str, history: list[dict[str, str]]): 40 | self.name: str = name 41 | self.insights: str = insights 42 | self.history: list[dict] = history 43 | 44 | @cache 45 | def template(self) -> list[dict]: 46 | return [ 47 | { 48 | "role": "user", 49 | "content": f"""${bannerString}`; 18 | banner.push(eleToPush); 19 | }); 20 | banner.push("
YouSim is a fun open-ended demo to explore the multiverse of identities
to glimpse a (mere infinite) sliver of the (transfinite) diversity within the latent space.
Inspired by WorldSim, WebSim, & Infinite Backrooms, YouSim leverages Claude to let you locate, modify, & interact with any entity you can imagine.
It's a game that can simulate anyone you like.
Who will you summon?