feat: solution for 69b1a07c67bbf488a1177da4
This commit is contained in:
@@ -0,0 +1,103 @@
|
|||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
from pydantic import SecretStr
|
||||||
|
from langgraph.graph import StateGraph, START, END
|
||||||
|
from langgraph.checkpoint.memory import InMemorySaver
|
||||||
|
from langgraph.types import interrupt, Command
|
||||||
|
import questionary
|
||||||
|
|
||||||
|
# LLM configuration with placeholders
|
||||||
|
llm = ChatOpenAI(
|
||||||
|
model="openai/gpt-oss-20b",
|
||||||
|
base_url='https://platform.brojs.ru/jrnl-bh/api/inference/v1',
|
||||||
|
api_key=SecretStr("jrnl_30283ab953615cbb6846ff9940a1eedce0b76d7b2f59a2394f29e74643e6a90d"),
|
||||||
|
temperature=0.7,
|
||||||
|
)
|
||||||
|
|
||||||
|
# State definition
|
||||||
|
class GameState(dict):
|
||||||
|
topic: str | None = None
|
||||||
|
intro: str | None = None
|
||||||
|
options: list[str] | None = None
|
||||||
|
choice: str | None = None
|
||||||
|
ending: str | None = None
|
||||||
|
|
||||||
|
# Node to generate intro and options
|
||||||
|
def generate_intro(state: GameState) -> GameState:
|
||||||
|
topic = state.get("topic", "неизвестная тема")
|
||||||
|
prompt = f"""Тема: {topic}. Придумай короткую завязку (2–3 предложения) и ровно 3 варианта действий героя. Ответь в формате:
|
||||||
|
Завязка:
|
||||||
|
<текст>
|
||||||
|
Варианты:
|
||||||
|
1) <вариант1>
|
||||||
|
2) <вариант2>
|
||||||
|
3) <вариант3>"""
|
||||||
|
response = llm.invoke(prompt).content
|
||||||
|
# Parse response
|
||||||
|
parts = response.split("Варианты:")
|
||||||
|
intro_part = parts[0].replace("Завязка:", "").strip()
|
||||||
|
options_part = parts[1] if len(parts) > 1 else ""
|
||||||
|
options = [opt.strip() for opt in options_part.splitlines() if opt.strip()]
|
||||||
|
state["intro"] = intro_part
|
||||||
|
state["options"] = options
|
||||||
|
# Interrupt to ask user
|
||||||
|
return interrupt(
|
||||||
|
{
|
||||||
|
"type": "choice",
|
||||||
|
"question": f"{intro_part}\n\nЧто делаем?",
|
||||||
|
"options": options,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Node to handle resume after choice and generate ending
|
||||||
|
def generate_ending(state: GameState) -> GameState:
|
||||||
|
intro = state.get("intro", "")
|
||||||
|
choice = state.get("choice", "")
|
||||||
|
prompt = f"""Завязка: {intro}
|
||||||
|
Выбор пользователя: {choice}
|
||||||
|
Допиши короткую концовку (2–3 предложения)."""
|
||||||
|
ending = llm.invoke(prompt).content.strip()
|
||||||
|
state["ending"] = ending
|
||||||
|
return state
|
||||||
|
|
||||||
|
# Build graph
|
||||||
|
graph_builder = StateGraph(GameState)
|
||||||
|
graph_builder.add_node("generate_intro", generate_intro)
|
||||||
|
graph_builder.add_node("generate_ending", generate_ending)
|
||||||
|
graph_builder.set_entry_point("generate_intro")
|
||||||
|
graph_builder.add_edge(START, "generate_intro")
|
||||||
|
graph_builder.add_edge("generate_intro", "generate_ending") # continue after interrupt
|
||||||
|
graph_builder.add_edge("generate_ending", END)
|
||||||
|
|
||||||
|
# Compile with checkpoint
|
||||||
|
checkpoint = InMemorySaver()
|
||||||
|
app = graph_builder.compile(checkpointer=checkpoint)
|
||||||
|
|
||||||
|
def run_game(topic: str):
|
||||||
|
state = GameState({"topic": topic})
|
||||||
|
config = {"configurable": {"thread_id": "game_thread"}}
|
||||||
|
# First stream until interrupt
|
||||||
|
for chunk in app.stream(state, config):
|
||||||
|
if "__interrupt__" in chunk:
|
||||||
|
payload = chunk["__interrupt__"][0].value # dict from interrupt()
|
||||||
|
answer = questionary.select(
|
||||||
|
payload["question"],
|
||||||
|
choices=payload["options"]
|
||||||
|
).ask()
|
||||||
|
# Resume with user's choice
|
||||||
|
resume_payload = {"choice": answer}
|
||||||
|
for res_chunk in app.stream(Command(resume=resume_payload), config):
|
||||||
|
if "ending" in res_chunk:
|
||||||
|
print("\n[LLM] Концовка:", res_chunk["ending"])
|
||||||
|
# Final state
|
||||||
|
final_state = checkpoint.get("game_thread")
|
||||||
|
print("\nИтоговое состояние:")
|
||||||
|
print("Тема:", final_state.get("topic"))
|
||||||
|
print("Завязка:", final_state.get("intro"))
|
||||||
|
print("Выбор:", final_state.get("choice"))
|
||||||
|
print("Концовка:", final_state.get("ending"))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
topic_input = input("Введите тему истории: ").strip()
|
||||||
|
if not topic_input:
|
||||||
|
topic_input = "космический кот"
|
||||||
|
run_game(topic_input)
|
||||||
Reference in New Issue
Block a user