diff --git a/frontend/src/components/AppArtifacts.vue b/frontend/src/components/AppArtifacts.vue index 2d08b4c..273fc1a 100644 --- a/frontend/src/components/AppArtifacts.vue +++ b/frontend/src/components/AppArtifacts.vue @@ -81,9 +81,13 @@ const acceptFiles = async (files: any[]) => { if (uploadedFileName.value === "") { uploadedFileName.value = file.filename; } - + const name = uploadedFileName.value.trim() || file.filename try { - const response = await uploadArtifact(props.session, file.filename, uploadContent, file.mimetype) + const response = await uploadArtifact( + props.session, + name, + uploadContent, + file.mimetype) console.log("File uploaded successfully:", response); @@ -94,7 +98,7 @@ const acceptFiles = async (files: any[]) => { // add it to the list of artifacts artifacts.value.push({ - filename: `Figure ${desc.figure_number}`, + filename: name, title: desc.title, description: desc.description, mimetype: file.mimetype, diff --git a/packages/.DS_Store b/packages/.DS_Store new file mode 100644 index 0000000..48b6206 Binary files /dev/null and b/packages/.DS_Store differ diff --git a/packages/manugen-ai/.DS_Store b/packages/manugen-ai/.DS_Store new file mode 100644 index 0000000..9b0324a Binary files /dev/null and b/packages/manugen-ai/.DS_Store differ diff --git a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/assembler/agent.py b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/assembler/agent.py index 3ae6705..8109c2b 100644 --- a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/assembler/agent.py +++ b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/assembler/agent.py @@ -29,6 +29,14 @@ def manuscript_assembler(callback_context: CallbackContext) -> Optional[types.Co # then remove the instruction key from the state callback_context.state[instructions_key] = "" + + # ---------- Append Supplementary Figures section, if any ---------- + if "supplementary_figures" in current_state and current_state["supplementary_figures"].strip(): + manuscript_content += ( + "\n# Supplementary Figures\n\n" + f"{current_state['supplementary_figures']}" + ) + # ------------------------------------------------------------------ return types.Content( parts=[types.Part(text=manuscript_content)], diff --git a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/coordinator/custom_agent.py b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/coordinator/custom_agent.py index 3d841b9..7aa372d 100644 --- a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/coordinator/custom_agent.py +++ b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/coordinator/custom_agent.py @@ -75,9 +75,16 @@ async def _run_async_impl( # simulate an event that there was a transfer of agent yield self.get_transfer_to_agent_event(ctx, agent) - # remove "display_name" since adk does not support it - if last_user_input.inline_data is not None: - last_user_input.inline_data.display_name = None + # --- if the user gave a filename, inject it as a text part --- + if last_user_input.inline_data and last_user_input.inline_data.display_name: + from google.genai.types import Part + file_name = last_user_input.inline_data.display_name + # just inject the “Filename:” hint so the LLM sees it + ctx.user_content.parts.insert( + 0, + Part(text=f"Filename: {file_name}") + ) + # ------------------------------------------------------------------ # call agent async for event in agent.run_async(ctx): diff --git a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/discussion/prompt.py b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/discussion/prompt.py index 9800166..8488c03 100644 --- a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/discussion/prompt.py +++ b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/discussion/prompt.py @@ -73,6 +73,10 @@ deal with these weaknesses. The fifth paragraph may then culminate in a description of how the paper moves the field forward. Step by step, the reader thus learns to put the paper’s conclusions into the right context. + +* When referring to data presented in supplementary figures (i.e., those figures with +the words Supp., Supplemental, S, etc.), cite them with the format the user used, +for example “Figure S1” is Figure S1, "Supplemental Figure 1" is Suplemental Figure 1, etc. ``` # Output diff --git a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/figure/agent.py b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/figure/agent.py index 08a61ed..bfccf19 100644 --- a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/figure/agent.py +++ b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/figure/agent.py @@ -38,10 +38,21 @@ def process_figure_response( # get current state state = callback_context.state - # get agent description about figure - figure_desc = llm_response.content.parts[0].text - figure_desc_obj = SingleFigureDescription.model_validate_json(figure_desc) - + # parse the LLM’s JSON into our Pydantic model + raw = llm_response.content.parts[0].text + figure_desc_obj = SingleFigureDescription.model_validate_json(raw) + + # locate the uploaded‐file part anywhere in user_content.parts + incoming = None + for part in callback_context._invocation_context.user_content.parts: + if getattr(part, "inline_data", None) is not None: + incoming = part.inline_data + break + + # if the user gave a filename, stash it and override figure title + if incoming and getattr(incoming, "display_name", None): + callback_context.state["current_display_name"] = incoming.display_name + figure_desc_obj.title = incoming.display_name # compute figure number current_figure_id = 1 if FIGURES_KEY in state: @@ -75,16 +86,20 @@ def update_figure_state( current_figure = state[CURRENT_FIGURE_KEY] current_figure_obj = SingleFigureDescription.model_validate(current_figure) + # load or initialize the figures dict, then store it back on state + current_figure_state = state.get(FIGURES_KEY, {}) + state[FIGURES_KEY] = current_figure_state - if FIGURES_KEY not in state: - state[FIGURES_KEY] = {} - - current_figure_state = state[FIGURES_KEY] - current_figure_state[current_figure_obj.figure_number] = { - k: v for k, v in current_figure_obj.model_dump().items() if k != "figure_number" + entry = { + k: v + for k, v in current_figure_obj.model_dump().items() + if k != "figure_number" } - - state[FIGURES_KEY] = current_figure_state + # preserve any user‑provided filename exactly + display_label = state.get("current_display_name", None) + if display_label is not None: + entry["display_name"] = display_label + current_figure_state[current_figure_obj.figure_number] = entry state[CURRENT_FIGURE_KEY] = "" diff --git a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/figure/prompt.py b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/figure/prompt.py index 6a02909..f9ab3a7 100644 --- a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/figure/prompt.py +++ b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/figure/prompt.py @@ -1,4 +1,4 @@ -"""Prompt for the abstract_agent""" +"""Prompt for the figure agent""" import json @@ -13,6 +13,9 @@ * Analyze the figure in the context of a scientific paper. * Generate an in-depth description of the figure. If you need to add formatting to the description, use Markdown. +* If the figure’s name (title or file name) contains “Supp.“, “supp.“, “Supplemental“, +“supplemental“, “Supplementary“, "S", or any clear variant indicating it is a supplementary figure, +then set "figure_type": "supplemental"; otherwise leave it as "main". * Generate a short title for the figure. If you need to add formatting to the title, use Markdown. * Respond ONLY with a JSON object matching this schema: diff --git a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/introduction/prompt.py b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/introduction/prompt.py index f66f3e7..fb43227 100644 --- a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/introduction/prompt.py +++ b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/introduction/prompt.py @@ -85,6 +85,10 @@ the following ways: it does not need to present the context (which has just been given), it is somewhat more specific about the results, and it only briefly previews the conclusion of the paper, if at all. + +* When referring to data presented in supplementary figures (i.e., those figures with +the words Supp., Supplemental, S, etc.), cite them with the format the user used, +for example “Figure S1” is Figure S1, "Supplemental Figure 1" is Suplemental Figure 1, etc. ``` # Output diff --git a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/methods/prompt.py b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/methods/prompt.py index 32dfaaa..7b2e69e 100644 --- a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/methods/prompt.py +++ b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/methods/prompt.py @@ -100,6 +100,10 @@ give exact volumes, times, and temperatures. Clarity: avoid jargon; define any uncommon abbreviation at first use. Self-contained: every reagent or piece of equipment needed to reproduce should be listed here or in a table. + +* When referring to data presented in supplementary figures (i.e., those figures with +the words Supp., Supplemental, S, etc.), cite them with the format the user used, +for example “Figure S1” is Figure S1, "Supplemental Figure 1" is Suplemental Figure 1, etc. ``` # Output diff --git a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/results/prompt.py b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/results/prompt.py index f75bc2e..b3ce7fd 100644 --- a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/results/prompt.py +++ b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/results/prompt.py @@ -87,6 +87,11 @@ statement, and paragraphs farther down in the text rely on the logical conclusions of previous paragraphs, much as theorems are built in mathematical literature. + +* When referring to data presented in supplementary figures (i.e., those figures with +the words Supp., Supplemental, S, etc.), cite them with the format the user used, +for example “Figure S1” is Figure S1, "Supplemental Figure 1" is Suplemental Figure 1, etc. + ``` # Output diff --git a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/reviewer/agent.py b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/reviewer/agent.py index d96629a..6f0a507 100644 --- a/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/reviewer/agent.py +++ b/packages/manugen-ai/src/manugen_ai/agents/ai_science_writer/sub_agents/reviewer/agent.py @@ -25,6 +25,8 @@ Your job: 1. Provide a concise bullet-list of any changes needed. 2. If **no** changes are needed (content is publication-ready), call the tool `exit_loop` instead of returning bullets. +3. Ensure there are no captions in `{supplementary_figures}` that lack a matching citation + Return either: - A JSON list of feedback bullets, diff --git a/packages/manugen-ai/src/manugen_ai/schema.py b/packages/manugen-ai/src/manugen_ai/schema.py index a3f5723..7819703 100644 --- a/packages/manugen-ai/src/manugen_ai/schema.py +++ b/packages/manugen-ai/src/manugen_ai/schema.py @@ -25,14 +25,15 @@ def prepare_instructions(callback_context: CallbackContext) -> Optional[types.Co callback_context.state[key1] = "" # add figures descriptions - figure_descriptions = "" + callback_context.state[FIGURES_DESCRIPTIONS_KEY] = "" if FIGURES_KEY in current_state: - current_figure_state = current_state[FIGURES_KEY] - for figure_number, figure_data in current_figure_state.items(): - figure_descriptions += f"Figure {figure_number}: {figure_data['title']}\n{figure_data['description']}\n\n" - - callback_context.state[FIGURES_DESCRIPTIONS_KEY] = figure_descriptions.strip() - + figure_descriptions = "" + for num, fig in current_state[FIGURES_KEY].items(): + label = fig.get("display_name", f"Figure {num}") + figure_descriptions += ( + f"{label}: {fig['title']}\n{fig['description']}\n\n" + ) + callback_context.state[FIGURES_DESCRIPTIONS_KEY] = figure_descriptions.strip() class ManuscriptStructure(BaseModel): title: str = Field(default="")