Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -141,27 +141,16 @@ for d in (
|
|
| 141 |
# JSON_DIR,
|
| 142 |
):
|
| 143 |
d.mkdir(parents=True, exist_ok=True)
|
| 144 |
-
# def classify_image_type(description_or_name: str) -> str:
|
| 145 |
-
# desc = description_or_name.lower()
|
| 146 |
-
|
| 147 |
-
# sprite_keywords = ["sprite", "character", "animal", "person", "creature", "robot", "figure"]
|
| 148 |
-
# backdrop_keywords = ["background", "scene", "forest", "city", "room", "sky", "mountain", "village"]
|
| 149 |
-
# code_block_keywords = [
|
| 150 |
-
# "move", "turn", "wait", "repeat", "if", "else", "broadcast",
|
| 151 |
-
# "glide", "change", "forever", "when", "switch", "costume",
|
| 152 |
-
# "say", "think", "stop", "clone", "touching", "sensing",
|
| 153 |
-
# "scratch", "block", "code", "set", "variable"
|
| 154 |
-
# ]
|
| 155 |
-
|
| 156 |
-
# if any(kw in desc for kw in code_block_keywords):
|
| 157 |
-
# return "code-block"
|
| 158 |
-
# elif any(kw in desc for kw in sprite_keywords):
|
| 159 |
-
# return "sprite"
|
| 160 |
-
# elif any(kw in desc for kw in backdrop_keywords):
|
| 161 |
-
# return "backdrop"
|
| 162 |
-
# else:
|
| 163 |
-
# return "unknown"
|
| 164 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
class GameState(TypedDict):
|
| 166 |
project_json: dict
|
| 167 |
description: str
|
|
@@ -170,10 +159,9 @@ class GameState(TypedDict):
|
|
| 170 |
pseudo_code: dict
|
| 171 |
action_plan: Optional[Dict]
|
| 172 |
temporary_node: Optional[Dict]
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
# pseudo_node: Optional[Dict]
|
| 177 |
|
| 178 |
# Refined SYSTEM_PROMPT with more explicit Scratch JSON rules, especially for variables
|
| 179 |
SYSTEM_PROMPT = """
|
|
@@ -278,25 +266,6 @@ agent_json_resolver = create_react_agent(
|
|
| 278 |
prompt=SYSTEM_PROMPT_JSON_CORRECTOR
|
| 279 |
)
|
| 280 |
|
| 281 |
-
# # Helper function to load the block catalog from a JSON file
|
| 282 |
-
# def _load_block_catalog(file_path: str) -> Dict:
|
| 283 |
-
# """Loads the Scratch block catalog from a specified JSON file."""
|
| 284 |
-
# try:
|
| 285 |
-
# with open(file_path, 'r') as f:
|
| 286 |
-
# catalog = json.load(f)
|
| 287 |
-
# logger.info(f"Successfully loaded block catalog from {file_path}")
|
| 288 |
-
# return catalog
|
| 289 |
-
# except FileNotFoundError:
|
| 290 |
-
# logger.error(f"Error: Block catalog file not found at {file_path}")
|
| 291 |
-
# # Return an empty dict or raise an error, depending on desired behavior
|
| 292 |
-
# return {}
|
| 293 |
-
# except json.JSONDecodeError as e:
|
| 294 |
-
# logger.error(f"Error decoding JSON from {file_path}: {e}")
|
| 295 |
-
# return {}
|
| 296 |
-
# except Exception as e:
|
| 297 |
-
# logger.error(f"An unexpected error occurred while loading {file_path}: {e}")
|
| 298 |
-
# return {}
|
| 299 |
-
|
| 300 |
# Helper function to load the block catalog from a JSON file
|
| 301 |
def _load_block_catalog(block_type: str) -> Dict:
|
| 302 |
"""
|
|
@@ -696,7 +665,6 @@ def extract_json_from_llm_response(raw_response: str) -> dict:
|
|
| 696 |
# # 6. Return with the correct data URI prefix
|
| 697 |
# return f"data:image/png;base64,{clean_b64}"
|
| 698 |
|
| 699 |
-
# reducing imagebase64 size if greater than something
|
| 700 |
def reduce_image_size_to_limit(clean_b64_str, max_kb=4000):
|
| 701 |
"""
|
| 702 |
Reduce an image's size to be as close as possible to max_kb without exceeding it.
|
|
@@ -762,7 +730,7 @@ def clean_base64_for_model(raw_b64):
|
|
| 762 |
# Log original size
|
| 763 |
original_size = len(clean_b64.encode("utf-8"))
|
| 764 |
print(f"Original Base64 size (bytes): {original_size}")
|
| 765 |
-
if original_size> 4000000:
|
| 766 |
# Reduce size to under 4 MB
|
| 767 |
reduced_b64 = reduce_image_size_to_limit(clean_b64, max_kb=4000)
|
| 768 |
clean_b64_2 = re.sub(r"^data:image\/[a-zA-Z]+;base64,", "", reduced_b64)
|
|
@@ -772,7 +740,7 @@ def clean_base64_for_model(raw_b64):
|
|
| 772 |
# Return both prefixed and clean reduced versions
|
| 773 |
return f"data:image/jpeg;base64,{reduced_b64}"
|
| 774 |
return f"data:image/jpeg;base64,{clean_b64}"
|
| 775 |
-
|
| 776 |
def format_scratch_pseudo_code(code_string):
|
| 777 |
"""
|
| 778 |
Parses and formats Scratch pseudo-code with correct indentation,
|
|
@@ -823,16 +791,24 @@ def format_scratch_pseudo_code(code_string):
|
|
| 823 |
|
| 824 |
return '\n'.join(formatted_lines)
|
| 825 |
|
| 826 |
-
|
| 827 |
# Node 1: Logic updating if any issue here
|
| 828 |
def pseudo_generator_node(state: GameState):
|
| 829 |
logger.info("--- Running plan_logic_aligner_node ---")
|
| 830 |
image = state.get("project_image", "")
|
| 831 |
project_json = state["project_json"]
|
| 832 |
-
|
|
|
|
| 833 |
# MODIFICATION 1: Include 'Stage' in the list of names to plan for.
|
| 834 |
# It's crucial to ensure 'Stage' is always present for its global role.
|
| 835 |
target_names = [t["name"] for t in project_json["targets"]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 836 |
|
| 837 |
refinement_prompt = f"""
|
| 838 |
You are an expert in Scratch 3.0 game development, specializing in understanding block relationships (stacked, nested).
|
|
@@ -840,8 +816,15 @@ You are an expert in Scratch 3.0 game development, specializing in understanding
|
|
| 840 |
From Image, you also have to detect a value of Key given in Text form "Script for: ". Below is the example
|
| 841 |
Example: "Script for: Bear", "Script for:" is a key and "Bear" is value and check if there is related target name available.
|
| 842 |
|
| 843 |
-
**
|
|
|
|
|
|
|
| 844 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 845 |
--- Scratch 3.0 Block Reference ---
|
| 846 |
### Hat Blocks
|
| 847 |
Description: {hat_description}
|
|
@@ -974,7 +957,7 @@ If you find any "Code-Blocks" then,
|
|
| 974 |
"type": "image_url",
|
| 975 |
"image_url": {
|
| 976 |
# "url": f"data:image/png;base64,{image}"
|
| 977 |
-
"url": clean_base64_for_model(image)
|
| 978 |
}
|
| 979 |
}
|
| 980 |
|
|
@@ -1018,10 +1001,12 @@ If you find any "Code-Blocks" then,
|
|
| 1018 |
|
| 1019 |
# Update the original action_plan in the state with the refined version
|
| 1020 |
state["pseudo_code"] = result
|
| 1021 |
-
|
|
|
|
| 1022 |
# with open("debug_state.json", "w", encoding="utf-8") as f:
|
| 1023 |
# json.dump(state, f, indent=2, ensure_ascii=False)
|
| 1024 |
print(f"[OVREALL REFINED PSEUDO CODE LOGIC]: {result}")
|
|
|
|
| 1025 |
logger.info("Plan refinement and block relation analysis completed for all plans.")
|
| 1026 |
return state
|
| 1027 |
# Node 2: planner node
|
|
@@ -1929,6 +1914,7 @@ def overall_block_builder_node_2(state: GameState):
|
|
| 1929 |
|
| 1930 |
# Node 6: variable adder node
|
| 1931 |
def variable_adder_node(state: GameState):
|
|
|
|
| 1932 |
project_json = state["project_json"]
|
| 1933 |
try:
|
| 1934 |
updated_project_json = variable_adder_main(project_json)
|
|
@@ -1938,239 +1924,23 @@ def variable_adder_node(state: GameState):
|
|
| 1938 |
else:
|
| 1939 |
print("Variable adder unable to add any variable inside the project!")
|
| 1940 |
state["project_json"]=project_json
|
|
|
|
| 1941 |
return state
|
| 1942 |
except Exception as e:
|
| 1943 |
logger.error(f"Error in variable adder node while updating project_json': {e}")
|
| 1944 |
raise
|
| 1945 |
|
| 1946 |
-
|
| 1947 |
-
|
| 1948 |
-
|
| 1949 |
-
|
| 1950 |
-
|
| 1951 |
-
|
| 1952 |
-
|
| 1953 |
-
|
| 1954 |
-
|
| 1955 |
-
|
| 1956 |
-
|
| 1957 |
-
# def plan_logic_aligner_node(state: GameState):
|
| 1958 |
-
# logger.info("--- Running plan_logic_aligner_node ---")
|
| 1959 |
-
|
| 1960 |
-
# image = state.get("image", "")
|
| 1961 |
-
|
| 1962 |
-
# refinement_prompt = f"""
|
| 1963 |
-
# You are an expert in Scratch 3.0 game development, specializing in understanding block relationships (stacked, nested).
|
| 1964 |
-
# "Analyze the Scratch code-block image and generate Pseudo-Code for what this logic appears to be doing."
|
| 1965 |
-
# From Image, you also have to detect a value of Key given in Text form "Script for: ". Below is the example
|
| 1966 |
-
# Example: "Script for: Bear", "Script for:" is a key and "Bear" is value.
|
| 1967 |
-
# --- Scratch 3.0 Block Reference ---
|
| 1968 |
-
# ### Hat Blocks
|
| 1969 |
-
# Description: {hat_description}
|
| 1970 |
-
# Blocks:
|
| 1971 |
-
# {hat_opcodes_functionalities}
|
| 1972 |
-
|
| 1973 |
-
# ### Boolean Blocks
|
| 1974 |
-
# Description: {boolean_description}
|
| 1975 |
-
# Blocks:
|
| 1976 |
-
# {boolean_opcodes_functionalities}
|
| 1977 |
-
|
| 1978 |
-
# ### C Blocks
|
| 1979 |
-
# Description: {c_description}
|
| 1980 |
-
# Blocks:
|
| 1981 |
-
# {c_opcodes_functionalities}
|
| 1982 |
-
|
| 1983 |
-
# ### Cap Blocks
|
| 1984 |
-
# Description: {cap_description}
|
| 1985 |
-
# Blocks:
|
| 1986 |
-
# {cap_opcodes_functionalities}
|
| 1987 |
-
|
| 1988 |
-
# ### Reporter Blocks
|
| 1989 |
-
# Description: {reporter_description}
|
| 1990 |
-
# Blocks:
|
| 1991 |
-
# {reporter_opcodes_functionalities}
|
| 1992 |
-
|
| 1993 |
-
# ### Stack Blocks
|
| 1994 |
-
# Description: {stack_description}
|
| 1995 |
-
# Blocks:
|
| 1996 |
-
# {stack_opcodes_functionalities}
|
| 1997 |
-
# -----------------------------------
|
| 1998 |
-
|
| 1999 |
-
# Your task is to:
|
| 2000 |
-
# If you don't find any "Code-Blocks" then,
|
| 2001 |
-
# **Don't generate Pseudo Code, and pass the message "No Code-blocks" find...
|
| 2002 |
-
# If you find any "Code-Blocks" then,
|
| 2003 |
-
# 1. **Refine the 'logic'**: Make it precise, accurate, and fully aligned with the Game Description. Use Scratch‑consistent verbs and phrasing. **Do NOT** use raw double‑quotes inside the logic string.
|
| 2004 |
-
|
| 2005 |
-
# 2. **Structural requirements**:
|
| 2006 |
-
# - **Numeric values** `(e.g., 0, 5, 0.2, -130)` **must** be in parentheses: `(0)`, `(5)`, `(0.2)`, `(-130)`.
|
| 2007 |
-
# - **AlphaNumeric values** `(e.g., hello, say 5, 4, hi!)` **must** be in parentheses: `(hello)`, `(say 5)`, `(4)`, `(hi!)`.
|
| 2008 |
-
# - **Variables** must be in the form `[variable v]` (e.g., `[score v]`), even when used inside expressions two example use `set [score v] to (1)` or `show variable ([speed v])`.
|
| 2009 |
-
# - **Dropdown options** must be in the form `[option v]` (e.g., `[Game Start v]`, `[blue sky v]`). example use `when [space v] key pressed`.
|
| 2010 |
-
# - **Reporter blocks** used as inputs must be double‑wrapped: `((x position))`, `((y position))`. example use `if <((y position)) = (-130)> then` or `(((x position)) * (1))`.
|
| 2011 |
-
# - **Boolean blocks** in conditions must be inside `< >`, including nested ones: `<not <condition>>`, `<<cond1> and <cond2>>`,`<<cond1> or <cond2>>`.
|
| 2012 |
-
# - **Other Boolean blocks** in conditions must be inside `< >`, including nested ones or values or variables: `<(block/value/variable) * (block/value/variable)>`,`<(block/value/variable) < (block/value/variable)>`, and example of another variable`<[apple v] contains [a v]?>`.
|
| 2013 |
-
# - **Operator expressions** must use explicit Scratch operator blocks, e.g.:
|
| 2014 |
-
# ```
|
| 2015 |
-
# (([ballSpeed v]) * (1.1))
|
| 2016 |
-
# ```
|
| 2017 |
-
# - **Every hat block script must end** with a final `end` on its own line.
|
| 2018 |
-
|
| 2019 |
-
# 3. **Pseudo‑code formatting**:
|
| 2020 |
-
# - Represent each block or nested block on its own line.
|
| 2021 |
-
# - Indent nested blocks by 4 spaces under their parent (`forever`, `if`, etc.).
|
| 2022 |
-
# - No comments or explanatory text—just the block sequence.
|
| 2023 |
-
# - a natural language breakdown of each step taken after the event, formatted as a multi-line string representing pseudo-code. Ensure clarity and granularity—each described action should map closely to a Scratch block or tight sequence.
|
| 2024 |
-
|
| 2025 |
-
# 4. **Logic content**:
|
| 2026 |
-
# - Build clear flow for mechanics (movement, jumping, flying, scoring, collisions).
|
| 2027 |
-
# - Match each action closely to a Scratch block or tight sequence.
|
| 2028 |
-
# - Do **NOT** include any justification or comments—only the raw logic.
|
| 2029 |
-
|
| 2030 |
-
# 5. **Examples for reference**:
|
| 2031 |
-
# **Correct** pattern for a simple start script:
|
| 2032 |
-
# ```
|
| 2033 |
-
# when green flag clicked
|
| 2034 |
-
# switch backdrop to [blue sky v]
|
| 2035 |
-
# set [score v] to (0)
|
| 2036 |
-
# show variable [score v]
|
| 2037 |
-
# broadcast [Game Start v]
|
| 2038 |
-
# end
|
| 2039 |
-
# ```
|
| 2040 |
-
# **Correct** pattern for updating the high score variable handling:
|
| 2041 |
-
# ```
|
| 2042 |
-
# when I receive [Game Over v]
|
| 2043 |
-
# if <((score)) > (([High Score v]))> then
|
| 2044 |
-
# set [High Score v] to ([score v])
|
| 2045 |
-
# end
|
| 2046 |
-
# switch backdrop to [Game Over v]
|
| 2047 |
-
# end
|
| 2048 |
-
# ```
|
| 2049 |
-
# **Correct** pattern for level up and increase difficulty use:
|
| 2050 |
-
# ```
|
| 2051 |
-
# when I receive [Level Up v]
|
| 2052 |
-
# change [level v] by (1)
|
| 2053 |
-
# set [ballSpeed v] to ((([ballSpeed v]) * (1.1)))
|
| 2054 |
-
# end
|
| 2055 |
-
# ```
|
| 2056 |
-
# **Correct** pattern for jumping mechanics use:
|
| 2057 |
-
# ```
|
| 2058 |
-
# when [space v] key pressed
|
| 2059 |
-
# if <((y position)) = (-100)> then
|
| 2060 |
-
# repeat (5)
|
| 2061 |
-
# change y by (100)
|
| 2062 |
-
# wait (0.1) seconds
|
| 2063 |
-
# change y by (-100)
|
| 2064 |
-
# wait (0.1) seconds
|
| 2065 |
-
# end
|
| 2066 |
-
# end
|
| 2067 |
-
# end
|
| 2068 |
-
# ```
|
| 2069 |
-
# **Correct** pattern for continuos moving objects use:
|
| 2070 |
-
# ```
|
| 2071 |
-
# when green flag clicked
|
| 2072 |
-
# go to x: (240) y: (-100)
|
| 2073 |
-
# set [speed v] to (-5)
|
| 2074 |
-
# show variable [speed v]
|
| 2075 |
-
# forever
|
| 2076 |
-
# change x by ([speed v])
|
| 2077 |
-
# if <((x position)) < (-240)> then
|
| 2078 |
-
# go to x: (240) y: (-100)
|
| 2079 |
-
# end
|
| 2080 |
-
# end
|
| 2081 |
-
# end
|
| 2082 |
-
# ```
|
| 2083 |
-
# **Correct** pattern for continuos moving objects use:
|
| 2084 |
-
# ```
|
| 2085 |
-
# when green flag clicked
|
| 2086 |
-
# go to x: (240) y: (-100)
|
| 2087 |
-
# set [speed v] to (-5)
|
| 2088 |
-
# show variable [speed v]
|
| 2089 |
-
# forever
|
| 2090 |
-
# change x by ([speed v])
|
| 2091 |
-
# if <((x position)) < (-240)> then
|
| 2092 |
-
# go to x: (240) y: (-100)
|
| 2093 |
-
# end
|
| 2094 |
-
# end
|
| 2095 |
-
# end
|
| 2096 |
-
# ```
|
| 2097 |
-
# 6. **Donot** add any explaination of logic or comments to justify or explain just put the logic content in the json.
|
| 2098 |
-
# 7. **Output**:
|
| 2099 |
-
# Return **only** a JSON object, using double quotes everywhere:
|
| 2100 |
-
# ```json
|
| 2101 |
-
# {{
|
| 2102 |
-
# "refined_logic":{{
|
| 2103 |
-
# "name_variable": 'Value of "Sript for: "',
|
| 2104 |
-
# "pseudocode":"…your fully‑formatted pseudo‑code here…",
|
| 2105 |
-
# }}
|
| 2106 |
-
# }}
|
| 2107 |
-
# ```
|
| 2108 |
-
# """
|
| 2109 |
-
# image_input = {
|
| 2110 |
-
# "type": "image_url",
|
| 2111 |
-
# "image_url": {
|
| 2112 |
-
# "url": f"data:image/png;base64,{image}"
|
| 2113 |
-
# }
|
| 2114 |
-
# }
|
| 2115 |
-
|
| 2116 |
-
# content = [
|
| 2117 |
-
# {"type": "text", "text": refinement_prompt},
|
| 2118 |
-
# image_input
|
| 2119 |
-
# ]
|
| 2120 |
-
|
| 2121 |
-
# try:
|
| 2122 |
-
# # Invoke the main agent for logic refinement and relationship identification
|
| 2123 |
-
# response = agent.invoke({"messages": [{"role": "user", "content": content}]})
|
| 2124 |
-
# llm_output_raw = response["messages"][-1].content.strip()
|
| 2125 |
-
|
| 2126 |
-
# parsed_llm_output = extract_json_from_llm_response(llm_output_raw)
|
| 2127 |
-
|
| 2128 |
-
# # result = parsed_llm_output
|
| 2129 |
-
# # Extract needed values directly
|
| 2130 |
-
# logic_data = parsed_llm_output.get("refined_logic", {})
|
| 2131 |
-
# name_variable = logic_data.get("name_variable", "Unknown")
|
| 2132 |
-
# pseudocode = logic_data.get("pseudocode", "No logic extracted")
|
| 2133 |
-
|
| 2134 |
-
# result = {"pseudo_node": {
|
| 2135 |
-
# "name_variable": name_variable,
|
| 2136 |
-
# "pseudocode": pseudocode
|
| 2137 |
-
# }}
|
| 2138 |
-
|
| 2139 |
-
# print(f"result:\n\n {result}")
|
| 2140 |
-
# return result
|
| 2141 |
-
# except Exception as e:
|
| 2142 |
-
# logger.error(f"❌ plan_logic_aligner_node failed: {str(e)}")
|
| 2143 |
-
# return {"error": str(e)}
|
| 2144 |
-
# except json.JSONDecodeError as error_json:
|
| 2145 |
-
# # If JSON parsing fails, use the json resolver agent
|
| 2146 |
-
# correction_prompt = (
|
| 2147 |
-
# "Your task is to correct the provided JSON string to ensure it is **syntactically perfect and adheres strictly to JSON rules**.\n"
|
| 2148 |
-
# "It must be a JSON object with `refined_logic` (string) and `block_relationships` (array of objects).\n"
|
| 2149 |
-
# f"- **Error Details**: {error_json}\n\n"
|
| 2150 |
-
# "**Strict Instructions for your response:**\n"
|
| 2151 |
-
# "1. **ONLY** output the corrected JSON. Do not include any other text or explanations.\n"
|
| 2152 |
-
# "2. Ensure all keys and string values are enclosed in **double quotes**. Escape internal quotes (`\\`).\n"
|
| 2153 |
-
# "3. No trailing commas. Correct nesting.\n\n"
|
| 2154 |
-
# "Here is the problematic JSON string to correct:\n"
|
| 2155 |
-
# f"```json\n{llm_output_raw}\n```\n"
|
| 2156 |
-
# "Corrected JSON:\n"
|
| 2157 |
-
# )
|
| 2158 |
-
# try:
|
| 2159 |
-
# correction_response = agent_json_resolver.invoke({"messages": [{"role": "user", "content": correction_prompt}]})
|
| 2160 |
-
# corrected_output = extract_json_from_llm_response(correction_response["messages"][-1].content)
|
| 2161 |
-
|
| 2162 |
-
# result = {
|
| 2163 |
-
# #"image_path": image_path,
|
| 2164 |
-
# "pseudo_code": corrected_output
|
| 2165 |
-
# }
|
| 2166 |
-
|
| 2167 |
-
# return result
|
| 2168 |
-
|
| 2169 |
-
# except Exception as e_corr:
|
| 2170 |
-
# logger.error(f"Failed to correct JSON output for even after retry: {e_corr}")
|
| 2171 |
-
|
| 2172 |
-
#def extract_images_from_pdf(pdf_path: Path, json_base_dir: Path, image_base_dir: Path):
|
| 2173 |
-
#def extract_images_from_pdf(pdf_path: Path, json_base_dir: Path):
|
| 2174 |
|
| 2175 |
# Prepare manipulated sprite JSON structure
|
| 2176 |
manipulated_json = {}
|
|
@@ -2395,7 +2165,7 @@ def similarity_matching(sprites_data: str, project_folder: str) -> str:
|
|
| 2395 |
# Copy matched backdrop assets + collect
|
| 2396 |
# =========================================
|
| 2397 |
backdrop_data = []
|
| 2398 |
-
|
| 2399 |
for backdrop_idx, matched_idx in enumerate(most_similar_indices):
|
| 2400 |
matched_image_path = folder_image_paths[matched_idx]
|
| 2401 |
matched_folder = os.path.dirname(matched_image_path)
|
|
@@ -2405,6 +2175,11 @@ def similarity_matching(sprites_data: str, project_folder: str) -> str:
|
|
| 2405 |
if not matched_folder.startswith(backdrop_base_path):
|
| 2406 |
continue
|
| 2407 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2408 |
logger.info(f"Matched backdrop: {matched_image_path}")
|
| 2409 |
|
| 2410 |
# 1) Copy the matched backdrop image itself
|
|
@@ -2464,8 +2239,15 @@ def similarity_matching(sprites_data: str, project_folder: str) -> str:
|
|
| 2464 |
# then backdrop as the Stage
|
| 2465 |
if backdrop_data:
|
| 2466 |
all_costumes, sounds = [], []
|
|
|
|
| 2467 |
for i, bd in enumerate(backdrop_data):
|
| 2468 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2469 |
if i == 0:
|
| 2470 |
sounds = bd.get("sounds", [])
|
| 2471 |
stage_obj={
|
|
@@ -2548,145 +2330,6 @@ def similarity_matching(sprites_data: str, project_folder: str) -> str:
|
|
| 2548 |
json.dump(final_project, f, indent=2)
|
| 2549 |
|
| 2550 |
return project_json_path
|
| 2551 |
-
# for sprite_idx, matched_idx in enumerate(most_similar_indices):
|
| 2552 |
-
# matched_image_path = folder_image_paths[matched_idx]
|
| 2553 |
-
# matched_image_path = os.path.normpath(matched_image_path)
|
| 2554 |
-
# print(" --------------------------------------1- matched_image_path ---------------------------------------",matched_image_path)
|
| 2555 |
-
# matched_folder = os.path.dirname(matched_image_path)
|
| 2556 |
-
# #folder_name = os.path.basename(matched_folder)
|
| 2557 |
-
# print(" --------------------------------------1- matched_folder ---------------------------------------",matched_folder)
|
| 2558 |
-
# if matched_folder in copied_folders:
|
| 2559 |
-
# continue
|
| 2560 |
-
# copied_folders.add(matched_folder)
|
| 2561 |
-
# logger.info(f"Matched image path: {matched_image_path}")
|
| 2562 |
-
|
| 2563 |
-
# sprite_json_path = os.path.join(matched_folder, 'sprite.json')
|
| 2564 |
-
# print(" --------------------------------------- sprite_json_path ---------------------------------------",sprite_json_path)
|
| 2565 |
-
# if not os.path.exists(sprite_json_path):
|
| 2566 |
-
# logger.warning(f"sprite.json not found in: {matched_folder}")
|
| 2567 |
-
# continue
|
| 2568 |
-
|
| 2569 |
-
# with open(sprite_json_path, 'r') as f:
|
| 2570 |
-
# sprite_data = json.load(f)
|
| 2571 |
-
# # print(f"SPRITE DATA: \n{sprite_data}")
|
| 2572 |
-
# # # Copy only non-matched files
|
| 2573 |
-
# # for fname in os.listdir(matched_folder):
|
| 2574 |
-
# # fpath = os.path.join(matched_folder, fname)
|
| 2575 |
-
# # if os.path.isfile(fpath) and fname not in {os.path.basename(matched_image_path), 'sprite.json'}:
|
| 2576 |
-
# # shutil.copy2(fpath, os.path.join(project_folder, fname))
|
| 2577 |
-
# # # logger.info(f"Copied Sprite asset: {fname}")
|
| 2578 |
-
# project_data.append(sprite_data)
|
| 2579 |
-
# print(" --------------------------------------1- project_data ---------------------------------------",project_data)
|
| 2580 |
-
# for fname in os.listdir(matched_folder):
|
| 2581 |
-
# fpath = os.path.join(matched_folder, fname)
|
| 2582 |
-
# dest_path = os.path.join(project_folder, fname)
|
| 2583 |
-
# if os.path.isfile(fpath) and fname not in {os.path.basename(matched_image_path), 'sprite.json'}:
|
| 2584 |
-
# shutil.copy2(fpath, dest_path)
|
| 2585 |
-
# logger.info(f"🟢 Copied Sprite Asset: {fpath} → {dest_path}")
|
| 2586 |
-
|
| 2587 |
-
# # ================================================================== #
|
| 2588 |
-
# # Loop through most similar images from Backdrops folder #
|
| 2589 |
-
# # → Copy Backdrop assets (excluding matched image + project.json) #
|
| 2590 |
-
# # → Load project.json and append its data to project_data #
|
| 2591 |
-
# # ================================================================== #
|
| 2592 |
-
# backdrop_data = [] # for backdrop-related entries
|
| 2593 |
-
|
| 2594 |
-
# for backdrop_idx, matched_idx in enumerate(most_similar_indices):
|
| 2595 |
-
# matched_image_path = os.path.normpath(folder_image_paths[matched_idx])
|
| 2596 |
-
# print(" --------------------------------------2- matched_image_path ---------------------------------------",matched_image_path)
|
| 2597 |
-
# # Check if the match is from the Backdrops folder
|
| 2598 |
-
# if matched_image_path.startswith(os.path.normpath(backdrop_images_path)):
|
| 2599 |
-
# matched_folder = os.path.dirname(matched_image_path)
|
| 2600 |
-
# print(" --------------------------------------2- matched_folder ---------------------------------------",matched_folder)
|
| 2601 |
-
# folder_name = os.path.basename(matched_folder)
|
| 2602 |
-
|
| 2603 |
-
# logger.info(f"Backdrop matched image: {matched_image_path}")
|
| 2604 |
-
|
| 2605 |
-
# # Copy only non-matched files
|
| 2606 |
-
# # for fname in os.listdir(matched_folder):
|
| 2607 |
-
# # fpath = os.path.join(matched_folder, fname)
|
| 2608 |
-
# # if os.path.isfile(fpath) and fname not in {os.path.basename(matched_image_path), 'project.json'}:
|
| 2609 |
-
# # shutil.copy2(fpath, os.path.join(project_folder, fname))
|
| 2610 |
-
# # # logger.info(f"Copied Backdrop asset: {fname}")
|
| 2611 |
-
# for fname in os.listdir(matched_folder):
|
| 2612 |
-
# fpath = os.path.join(matched_folder, fname)
|
| 2613 |
-
# dest_path = os.path.join(project_folder, fname)
|
| 2614 |
-
# if os.path.isfile(fpath) and fname not in {os.path.basename(matched_image_path), 'project.json'}:
|
| 2615 |
-
# shutil.copy2(fpath, dest_path)
|
| 2616 |
-
# logger.info(f"🟡 Copied Backdrop Asset: {fpath} → {dest_path}")
|
| 2617 |
-
|
| 2618 |
-
|
| 2619 |
-
# # Append backdrop's project.json
|
| 2620 |
-
# backdrop_json_path = os.path.join(matched_folder, 'project.json')
|
| 2621 |
-
# print(" --------------------------------------2- backdrop_json_path ---------------------------------------",backdrop_json_path)
|
| 2622 |
-
# if os.path.exists(backdrop_json_path):
|
| 2623 |
-
# with open(backdrop_json_path, 'r') as f:
|
| 2624 |
-
# backdrop_json_data = json.load(f)
|
| 2625 |
-
# # print(f"SPRITE DATA: \n{backdrop_json_data}")
|
| 2626 |
-
# if "targets" in backdrop_json_data:
|
| 2627 |
-
# for target in backdrop_json_data["targets"]:
|
| 2628 |
-
# if target.get("isStage") == True:
|
| 2629 |
-
# backdrop_data.append(target)
|
| 2630 |
-
# else:
|
| 2631 |
-
# logger.warning(f"project.json not found in: {matched_folder}")
|
| 2632 |
-
|
| 2633 |
-
# # Merge JSON structure
|
| 2634 |
-
# final_project = {
|
| 2635 |
-
# "targets": [],
|
| 2636 |
-
# "monitors": [],
|
| 2637 |
-
# "extensions": [],
|
| 2638 |
-
# "meta": {
|
| 2639 |
-
# "semver": "3.0.0",
|
| 2640 |
-
# "vm": "11.3.0",
|
| 2641 |
-
# "agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
| 2642 |
-
# }
|
| 2643 |
-
# }
|
| 2644 |
-
|
| 2645 |
-
# for sprite in project_data:
|
| 2646 |
-
# if not sprite.get("isStage", False):
|
| 2647 |
-
# final_project["targets"].append(sprite)
|
| 2648 |
-
|
| 2649 |
-
# if backdrop_data:
|
| 2650 |
-
# all_costumes, sounds = [], []
|
| 2651 |
-
# for idx, bd in enumerate(backdrop_data):
|
| 2652 |
-
# all_costumes.extend(bd.get("costumes", []))
|
| 2653 |
-
# if idx == 0 and "sounds" in bd:
|
| 2654 |
-
# sounds = bd["sounds"]
|
| 2655 |
-
# final_project["targets"].append({
|
| 2656 |
-
# "isStage": True,
|
| 2657 |
-
# "name": "Stage",
|
| 2658 |
-
# "variables": {},
|
| 2659 |
-
# "lists": {},
|
| 2660 |
-
# "broadcasts": {},
|
| 2661 |
-
# "blocks": {},
|
| 2662 |
-
# "comments": {},
|
| 2663 |
-
# "currentCostume": 1 if len(all_costumes) > 1 else 0,
|
| 2664 |
-
# "costumes": all_costumes,
|
| 2665 |
-
# "sounds": sounds,
|
| 2666 |
-
# "volume": 100,
|
| 2667 |
-
# "layerOrder": 0,
|
| 2668 |
-
# "tempo": 60,
|
| 2669 |
-
# "videoTransparency": 50,
|
| 2670 |
-
# "videoState": "on",
|
| 2671 |
-
# "textToSpeechLanguage": None
|
| 2672 |
-
# })
|
| 2673 |
-
|
| 2674 |
-
# with open(project_json_path, 'w') as f:
|
| 2675 |
-
# json.dump(final_project, f, indent=2)
|
| 2676 |
-
|
| 2677 |
-
# # logger.info(f"🎉 Final project saved: {project_json_path}")
|
| 2678 |
-
# return project_json_path
|
| 2679 |
-
|
| 2680 |
-
# def convert_bytes_to_image(pdf_bytes: bytes, dpi: int):
|
| 2681 |
-
# images = convert_from_bytes(pdf_bytes, dpi=dpi, poppler_path=poppler_path)
|
| 2682 |
-
# # Save each page to an in-memory BytesIO and return a list of BytesIOs
|
| 2683 |
-
# buffers = []
|
| 2684 |
-
# for img in images:
|
| 2685 |
-
# buf = BytesIO()
|
| 2686 |
-
# img.save(buf, format="PNG")
|
| 2687 |
-
# buf.seek(0)
|
| 2688 |
-
# buffers.append(buf)
|
| 2689 |
-
# return buffers
|
| 2690 |
|
| 2691 |
def convert_pdf_stream_to_images(pdf_stream: io.BytesIO, dpi=300):
|
| 2692 |
# Ensure we are at the start of the stream
|
|
@@ -2702,7 +2345,7 @@ def convert_pdf_stream_to_images(pdf_stream: io.BytesIO, dpi=300):
|
|
| 2702 |
|
| 2703 |
def delay_for_tpm_node(state: GameState):
|
| 2704 |
logger.info("--- Running DelayForTPMNode ---")
|
| 2705 |
-
time.sleep(
|
| 2706 |
logger.info("Delay completed.")
|
| 2707 |
return state
|
| 2708 |
|
|
@@ -2722,18 +2365,37 @@ workflow.add_node("refined_planner", refined_planner_node) # Refines the action
|
|
| 2722 |
workflow.add_node("opcode_counter", plan_opcode_counter_node)
|
| 2723 |
workflow.add_node("block_builder", overall_block_builder_node_2)
|
| 2724 |
workflow.add_node("variable_initializer", variable_adder_node)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2725 |
|
| 2726 |
-
workflow.set_entry_point("pseudo_generator")
|
| 2727 |
-
workflow.add_edge("pseudo_generator","time_delay_1")
|
| 2728 |
-
workflow.add_edge("time_delay_1","plan_generator")
|
| 2729 |
-
workflow.add_edge("plan_generator","time_delay_2")
|
| 2730 |
-
# workflow.add_edge("time_delay_2",END)
|
| 2731 |
-
workflow.add_edge("time_delay_2","refined_planner")
|
| 2732 |
-
workflow.add_edge("refined_planner","time_delay_3")
|
| 2733 |
-
workflow.add_edge("time_delay_3","opcode_counter")
|
| 2734 |
-
workflow.add_edge("opcode_counter","block_builder")
|
| 2735 |
-
workflow.add_edge("block_builder","variable_initializer")
|
| 2736 |
-
workflow.add_edge("variable_initializer", END)
|
| 2737 |
app_graph = workflow.compile()
|
| 2738 |
|
| 2739 |
# ============== Helper function to Upscale an Image ============== #
|
|
@@ -2911,6 +2573,11 @@ def process_pdf():
|
|
| 2911 |
print(f"-----------------------------Execution Time save_pdf_to_generated_dir() : {total_time}-----------------------------\n")
|
| 2912 |
# }
|
| 2913 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2914 |
# {
|
| 2915 |
# Extract & process
|
| 2916 |
# output_path, result = extract_images_from_pdf(saved_pdf_path)
|
|
@@ -2956,23 +2623,26 @@ def process_pdf():
|
|
| 2956 |
images = convert_pdf_stream_to_images(pdf_stream, dpi=300)
|
| 2957 |
else:
|
| 2958 |
images = convert_from_path(pdf_stream, dpi=300)
|
| 2959 |
-
|
| 2960 |
#updating logic here [Dev Patel]
|
| 2961 |
-
|
| 2962 |
-
|
| 2963 |
-
|
| 2964 |
-
|
| 2965 |
-
|
| 2966 |
-
|
| 2967 |
-
|
| 2968 |
-
|
| 2969 |
-
|
| 2970 |
-
|
|
|
|
|
|
|
|
|
|
| 2971 |
|
| 2972 |
-
#
|
| 2973 |
-
|
| 2974 |
-
|
| 2975 |
-
final_project_json = project_skeleton
|
| 2976 |
|
| 2977 |
# Save the *final* filled project JSON, overwriting the skeleton
|
| 2978 |
with open(project_output, "w") as f:
|
|
|
|
| 141 |
# JSON_DIR,
|
| 142 |
):
|
| 143 |
d.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
+
# class GameState(TypedDict):
|
| 146 |
+
# project_json: dict
|
| 147 |
+
# description: str
|
| 148 |
+
# project_id: str
|
| 149 |
+
# project_image: str
|
| 150 |
+
# pseudo_code: dict
|
| 151 |
+
# action_plan: Optional[Dict]
|
| 152 |
+
# temporary_node: Optional[Dict]
|
| 153 |
+
|
| 154 |
class GameState(TypedDict):
|
| 155 |
project_json: dict
|
| 156 |
description: str
|
|
|
|
| 159 |
pseudo_code: dict
|
| 160 |
action_plan: Optional[Dict]
|
| 161 |
temporary_node: Optional[Dict]
|
| 162 |
+
page_count: int
|
| 163 |
+
processing: bool
|
| 164 |
+
temp_pseudo_code: list
|
|
|
|
| 165 |
|
| 166 |
# Refined SYSTEM_PROMPT with more explicit Scratch JSON rules, especially for variables
|
| 167 |
SYSTEM_PROMPT = """
|
|
|
|
| 266 |
prompt=SYSTEM_PROMPT_JSON_CORRECTOR
|
| 267 |
)
|
| 268 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
# Helper function to load the block catalog from a JSON file
|
| 270 |
def _load_block_catalog(block_type: str) -> Dict:
|
| 271 |
"""
|
|
|
|
| 665 |
# # 6. Return with the correct data URI prefix
|
| 666 |
# return f"data:image/png;base64,{clean_b64}"
|
| 667 |
|
|
|
|
| 668 |
def reduce_image_size_to_limit(clean_b64_str, max_kb=4000):
|
| 669 |
"""
|
| 670 |
Reduce an image's size to be as close as possible to max_kb without exceeding it.
|
|
|
|
| 730 |
# Log original size
|
| 731 |
original_size = len(clean_b64.encode("utf-8"))
|
| 732 |
print(f"Original Base64 size (bytes): {original_size}")
|
| 733 |
+
if original_size > 4000000:
|
| 734 |
# Reduce size to under 4 MB
|
| 735 |
reduced_b64 = reduce_image_size_to_limit(clean_b64, max_kb=4000)
|
| 736 |
clean_b64_2 = re.sub(r"^data:image\/[a-zA-Z]+;base64,", "", reduced_b64)
|
|
|
|
| 740 |
# Return both prefixed and clean reduced versions
|
| 741 |
return f"data:image/jpeg;base64,{reduced_b64}"
|
| 742 |
return f"data:image/jpeg;base64,{clean_b64}"
|
| 743 |
+
|
| 744 |
def format_scratch_pseudo_code(code_string):
|
| 745 |
"""
|
| 746 |
Parses and formats Scratch pseudo-code with correct indentation,
|
|
|
|
| 791 |
|
| 792 |
return '\n'.join(formatted_lines)
|
| 793 |
|
|
|
|
| 794 |
# Node 1: Logic updating if any issue here
|
| 795 |
def pseudo_generator_node(state: GameState):
|
| 796 |
logger.info("--- Running plan_logic_aligner_node ---")
|
| 797 |
image = state.get("project_image", "")
|
| 798 |
project_json = state["project_json"]
|
| 799 |
+
cnt =state["page_count"]
|
| 800 |
+
print(f"The page number recived at the pseudo_generator node:-----> {cnt}")
|
| 801 |
# MODIFICATION 1: Include 'Stage' in the list of names to plan for.
|
| 802 |
# It's crucial to ensure 'Stage' is always present for its global role.
|
| 803 |
target_names = [t["name"] for t in project_json["targets"]]
|
| 804 |
+
stage_names = [t["name"] for t in project_json["targets"] if t.get("isStage")]
|
| 805 |
+
sprite_names = [t["name"] for t in project_json["targets"] if not t.get("isStage")]
|
| 806 |
+
# Get costumes separately for Stage and Sprites
|
| 807 |
+
stage_costumes = [
|
| 808 |
+
c["name"]
|
| 809 |
+
for t in project_json["targets"] if t.get("isStage")
|
| 810 |
+
for c in t.get("costumes", [])
|
| 811 |
+
]
|
| 812 |
|
| 813 |
refinement_prompt = f"""
|
| 814 |
You are an expert in Scratch 3.0 game development, specializing in understanding block relationships (stacked, nested).
|
|
|
|
| 816 |
From Image, you also have to detect a value of Key given in Text form "Script for: ". Below is the example
|
| 817 |
Example: "Script for: Bear", "Script for:" is a key and "Bear" is value and check if there is related target name available.
|
| 818 |
|
| 819 |
+
**Special Rule for Stage Costumes:**
|
| 820 |
+
If the value of "Script for:" exactly matches ANY costume name in the Stage_costumes list given below,
|
| 821 |
+
then the `"name_variable"` in the output JSON must be set to `"Stage"` (not the costume name).
|
| 822 |
|
| 823 |
+
**Targets in Game (Sprites and Stage) available in project_json:**
|
| 824 |
+
Sprite_name:{', '.join(sprite_names)}
|
| 825 |
+
Stage_name: Stage
|
| 826 |
+
Stage_costumes: {', '.join(stage_costumes)}
|
| 827 |
+
|
| 828 |
--- Scratch 3.0 Block Reference ---
|
| 829 |
### Hat Blocks
|
| 830 |
Description: {hat_description}
|
|
|
|
| 957 |
"type": "image_url",
|
| 958 |
"image_url": {
|
| 959 |
# "url": f"data:image/png;base64,{image}"
|
| 960 |
+
"url": clean_base64_for_model(image[cnt])
|
| 961 |
}
|
| 962 |
}
|
| 963 |
|
|
|
|
| 1001 |
|
| 1002 |
# Update the original action_plan in the state with the refined version
|
| 1003 |
state["pseudo_code"] = result
|
| 1004 |
+
state["temp_pseudo_code"] += [result]
|
| 1005 |
+
Data = state["temp_pseudo_code"]
|
| 1006 |
# with open("debug_state.json", "w", encoding="utf-8") as f:
|
| 1007 |
# json.dump(state, f, indent=2, ensure_ascii=False)
|
| 1008 |
print(f"[OVREALL REFINED PSEUDO CODE LOGIC]: {result}")
|
| 1009 |
+
print(f"[OVREALL LISTS OF LOGICS]: {Data}")
|
| 1010 |
logger.info("Plan refinement and block relation analysis completed for all plans.")
|
| 1011 |
return state
|
| 1012 |
# Node 2: planner node
|
|
|
|
| 1914 |
|
| 1915 |
# Node 6: variable adder node
|
| 1916 |
def variable_adder_node(state: GameState):
|
| 1917 |
+
logger.info("--- Running Variable Adder Node ---")
|
| 1918 |
project_json = state["project_json"]
|
| 1919 |
try:
|
| 1920 |
updated_project_json = variable_adder_main(project_json)
|
|
|
|
| 1924 |
else:
|
| 1925 |
print("Variable adder unable to add any variable inside the project!")
|
| 1926 |
state["project_json"]=project_json
|
| 1927 |
+
state["page_count"] +=1
|
| 1928 |
return state
|
| 1929 |
except Exception as e:
|
| 1930 |
logger.error(f"Error in variable adder node while updating project_json': {e}")
|
| 1931 |
raise
|
| 1932 |
|
| 1933 |
+
# Node 7: variable adder node
|
| 1934 |
+
def processed_page_node(state: GameState):
|
| 1935 |
+
logger.info("--- Processing the Pages Node ---")
|
| 1936 |
+
image = state.get("project_image", "")
|
| 1937 |
+
cnt =state["page_count"]
|
| 1938 |
+
print(f"The page processed for page:--------------> {cnt}")
|
| 1939 |
+
if cnt<len(image):
|
| 1940 |
+
state["processing"]= True
|
| 1941 |
+
else:
|
| 1942 |
+
state["processing"]= False
|
| 1943 |
+
return state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1944 |
|
| 1945 |
# Prepare manipulated sprite JSON structure
|
| 1946 |
manipulated_json = {}
|
|
|
|
| 2165 |
# Copy matched backdrop assets + collect
|
| 2166 |
# =========================================
|
| 2167 |
backdrop_data = []
|
| 2168 |
+
copied_backdrop_folders = set()
|
| 2169 |
for backdrop_idx, matched_idx in enumerate(most_similar_indices):
|
| 2170 |
matched_image_path = folder_image_paths[matched_idx]
|
| 2171 |
matched_folder = os.path.dirname(matched_image_path)
|
|
|
|
| 2175 |
if not matched_folder.startswith(backdrop_base_path):
|
| 2176 |
continue
|
| 2177 |
|
| 2178 |
+
# skip if backdrop folder already processed
|
| 2179 |
+
if matched_folder in copied_backdrop_folders:
|
| 2180 |
+
continue
|
| 2181 |
+
copied_backdrop_folders.add(matched_folder)
|
| 2182 |
+
|
| 2183 |
logger.info(f"Matched backdrop: {matched_image_path}")
|
| 2184 |
|
| 2185 |
# 1) Copy the matched backdrop image itself
|
|
|
|
| 2239 |
# then backdrop as the Stage
|
| 2240 |
if backdrop_data:
|
| 2241 |
all_costumes, sounds = [], []
|
| 2242 |
+
seen_costumes = set()
|
| 2243 |
for i, bd in enumerate(backdrop_data):
|
| 2244 |
+
for costume in bd.get("costumes", []):
|
| 2245 |
+
# Create a unique key for the costume
|
| 2246 |
+
key = (costume.get("name"), costume.get("assetId"))
|
| 2247 |
+
if key not in seen_costumes:
|
| 2248 |
+
seen_costumes.add(key)
|
| 2249 |
+
all_costumes.append(costume)
|
| 2250 |
+
|
| 2251 |
if i == 0:
|
| 2252 |
sounds = bd.get("sounds", [])
|
| 2253 |
stage_obj={
|
|
|
|
| 2330 |
json.dump(final_project, f, indent=2)
|
| 2331 |
|
| 2332 |
return project_json_path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2333 |
|
| 2334 |
def convert_pdf_stream_to_images(pdf_stream: io.BytesIO, dpi=300):
|
| 2335 |
# Ensure we are at the start of the stream
|
|
|
|
| 2345 |
|
| 2346 |
def delay_for_tpm_node(state: GameState):
|
| 2347 |
logger.info("--- Running DelayForTPMNode ---")
|
| 2348 |
+
time.sleep(10) # Adjust the delay as needed
|
| 2349 |
logger.info("Delay completed.")
|
| 2350 |
return state
|
| 2351 |
|
|
|
|
| 2365 |
workflow.add_node("opcode_counter", plan_opcode_counter_node)
|
| 2366 |
workflow.add_node("block_builder", overall_block_builder_node_2)
|
| 2367 |
workflow.add_node("variable_initializer", variable_adder_node)
|
| 2368 |
+
workflow.add_node("page_processed", processed_page_node)
|
| 2369 |
+
|
| 2370 |
+
workflow.set_entry_point("page_processed")
|
| 2371 |
+
# Conditional branching from the start
|
| 2372 |
+
def decide_next_step(state: GameState):
|
| 2373 |
+
if state.get("processing", False):
|
| 2374 |
+
return "pseudo_generator"
|
| 2375 |
+
else:
|
| 2376 |
+
return END
|
| 2377 |
+
|
| 2378 |
+
workflow.add_conditional_edges(
|
| 2379 |
+
"page_processed",
|
| 2380 |
+
decide_next_step,
|
| 2381 |
+
{
|
| 2382 |
+
"pseudo_generator": "pseudo_generator",
|
| 2383 |
+
str(END): END # str(END) is '__end__'
|
| 2384 |
+
}
|
| 2385 |
+
)
|
| 2386 |
+
# Main chain
|
| 2387 |
+
workflow.add_edge("pseudo_generator", "time_delay_1")
|
| 2388 |
+
workflow.add_edge("time_delay_1", "plan_generator")
|
| 2389 |
+
workflow.add_edge("plan_generator", "time_delay_2")
|
| 2390 |
+
workflow.add_edge("time_delay_2", "refined_planner")
|
| 2391 |
+
workflow.add_edge("refined_planner", "time_delay_3")
|
| 2392 |
+
workflow.add_edge("time_delay_3", "opcode_counter")
|
| 2393 |
+
workflow.add_edge("opcode_counter", "block_builder")
|
| 2394 |
+
workflow.add_edge("block_builder", "variable_initializer")
|
| 2395 |
+
|
| 2396 |
+
# After last node, check again
|
| 2397 |
+
workflow.add_edge("variable_initializer", "page_processed")
|
| 2398 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2399 |
app_graph = workflow.compile()
|
| 2400 |
|
| 2401 |
# ============== Helper function to Upscale an Image ============== #
|
|
|
|
| 2573 |
print(f"-----------------------------Execution Time save_pdf_to_generated_dir() : {total_time}-----------------------------\n")
|
| 2574 |
# }
|
| 2575 |
|
| 2576 |
+
# Save uploaded file to disk
|
| 2577 |
+
# pdf_path = os.path.join("/tmp", secure_filename(file.filename))
|
| 2578 |
+
# file.save(pdf_path)
|
| 2579 |
+
# compressed_pages = pdf_to_images_with_size_check(pdf_path, "/tmp/compressed_pages", size_limit_mb=4)
|
| 2580 |
+
|
| 2581 |
# {
|
| 2582 |
# Extract & process
|
| 2583 |
# output_path, result = extract_images_from_pdf(saved_pdf_path)
|
|
|
|
| 2623 |
images = convert_pdf_stream_to_images(pdf_stream, dpi=300)
|
| 2624 |
else:
|
| 2625 |
images = convert_from_path(pdf_stream, dpi=300)
|
| 2626 |
+
|
| 2627 |
#updating logic here [Dev Patel]
|
| 2628 |
+
initial_state_dict = {
|
| 2629 |
+
"project_json": project_skeleton,
|
| 2630 |
+
"description": "The pseudo code for the script",
|
| 2631 |
+
"project_id": project_id,
|
| 2632 |
+
# "project_image": img_b64,
|
| 2633 |
+
"project_image": images,
|
| 2634 |
+
"action_plan": {},
|
| 2635 |
+
"pseudo_code": {},
|
| 2636 |
+
"temporary_node": {},
|
| 2637 |
+
"processing":True,
|
| 2638 |
+
"page_count": 0,
|
| 2639 |
+
"temp_pseudo_code":[],
|
| 2640 |
+
}
|
| 2641 |
|
| 2642 |
+
#final_state_dict = app_graph.invoke(initial_state_dict) # Pass dictionary
|
| 2643 |
+
final_state_dict = app_graph.invoke(initial_state_dict,config={"recursion_limit": 200})
|
| 2644 |
+
final_project_json = final_state_dict['project_json'] # Access as dict
|
| 2645 |
+
# final_project_json = project_skeleton
|
| 2646 |
|
| 2647 |
# Save the *final* filled project JSON, overwriting the skeleton
|
| 2648 |
with open(project_output, "w") as f:
|