From 7ed5b7b9209b1e9b966d4f3acc0cc5516482c0b6 Mon Sep 17 00:00:00 2001 From: DivergAgent Date: Mon, 7 Jul 2025 15:24:22 +0200 Subject: [PATCH 1/4] Fix 1 SonarQube issues --- src/codeas/ui/components/documentation_ui.py | 282 ++++++++++++------- 1 file changed, 183 insertions(+), 99 deletions(-) diff --git a/src/codeas/ui/components/documentation_ui.py b/src/codeas/ui/components/documentation_ui.py index ae8e804..ed4f24c 100644 --- a/src/codeas/ui/components/documentation_ui.py +++ b/src/codeas/ui/components/documentation_ui.py @@ -60,74 +60,120 @@ def process_sections( preview: bool = False, use_previous: bool = False, ): - total_cost = 0 - total_input_tokens = 0 - total_output_tokens = 0 - total_input_cost = 0 - total_input_tokens = 0 + total_generate_cost = 0 + total_generate_input_tokens = 0 + total_generate_output_tokens = 0 + total_preview_input_cost = 0 + total_preview_input_tokens = 0 + + if 'outputs' not in st.session_state: + st.session_state.outputs = {} + full_documentation = "" with st.expander( f"Sections {'[Preview]' if preview else ''}", expanded=not use_previous ): for section in selected_sections: + output = None with st.spinner( - f"{'Generating' if generate else 'Previewing' if preview else 'Displaying'} {section}..." + f"{'Generating' if generate else 'Previewing' if preview else 'Displaying'} {section.replace('_', ' ').upper()}..." ): if generate: if use_previous: try: output = read_output(section) except FileNotFoundError: - output = run_generation(section) + try: + output = run_generation(section) + except Exception as e: + st.error(f"Error generating {section.replace('_', ' ').upper()}: {e}") + output = None + st.session_state.outputs.pop(section, None) else: - output = run_generation(section) - if output: - total_cost += output.cost["total_cost"] - total_input_tokens += output.tokens["input_tokens"] - total_output_tokens += output.tokens["output_tokens"] + try: + output = run_generation(section) + except Exception as e: + st.error(f"Error generating {section.replace('_', ' ').upper()}: {e}") + output = None + st.session_state.outputs.pop(section, None) + + + if output and hasattr(output, 'cost') and hasattr(output, 'tokens'): + total_generate_cost += output.cost.get("total_cost", 0) + total_generate_input_tokens += output.tokens.get("input_tokens", 0) + total_generate_output_tokens += output.tokens.get("output_tokens", 0) + elif preview: if use_previous: try: output = read_output(section) + if output and hasattr(output, 'cost') and hasattr(output, 'tokens'): + total_preview_input_cost += output.cost.get("input_cost", 0) + total_preview_input_tokens += output.tokens.get("input_tokens", 0) except FileNotFoundError: - output = run_preview(section) + try: + output = run_preview(section) + if output: + st.session_state.outputs[section] = output + if output and hasattr(output, 'cost') and hasattr(output, 'tokens'): + total_preview_input_cost += output.cost.get("input_cost", 0) + total_preview_input_tokens += output.tokens.get("input_tokens", 0) + except Exception as e: + st.error(f"Error previewing {section.replace('_', ' ').upper()}: {e}") + output = None + st.session_state.outputs.pop(section, None) + else: - output = run_preview(section) - if output: - total_input_cost += output.cost["input_cost"] - total_input_tokens += output.tokens["input_tokens"] + try: + output = run_preview(section) + if output: + st.session_state.outputs[section] = output + if output and hasattr(output, 'cost') and hasattr(output, 'tokens'): + total_preview_input_cost += output.cost.get("input_cost", 0) + total_preview_input_tokens += output.tokens.get("input_tokens", 0) + except Exception as e: + st.error(f"Error previewing {section.replace('_', ' ').upper()}: {e}") + output = None + st.session_state.outputs.pop(section, None) - display_section(section, generate, preview, use_previous) - if not preview and section in st.session_state.outputs: - cleaned_content = clean_markdown_content( - st.session_state.outputs[section].response["content"] - ) - full_documentation += f"\n\n{cleaned_content}" + display_section(section, generate, preview) + + if generate and section in st.session_state.outputs: + output_for_doc = st.session_state.outputs[section] + content_for_doc = getattr(getattr(output_for_doc, 'response', None), 'content', None) + if content_for_doc: + cleaned_content = clean_markdown_content(content_for_doc) + full_documentation += f"\n\n{cleaned_content}" + if generate: st.info( - f"Total cost: ${total_cost:.4f} " - f"(input tokens: {total_input_tokens:,}, " - f"output tokens: {total_output_tokens:,})" + f"Total cost: ${total_generate_cost:.4f} " + f"(input tokens: {total_generate_input_tokens:,}, " + f"output tokens: {total_generate_output_tokens:,})" ) if not use_previous: - usage_tracker.record_usage("generate_docs", total_cost) + if total_generate_cost > 0: + usage_tracker.record_usage("generate_docs", total_generate_cost) + else: + st.warning("No generation cost recorded as all sections failed or were skipped.") + elif preview: st.info( - f"Total input cost: ${total_input_cost:.4f} ({total_input_tokens:,} tokens)" + f"Total input cost: ${total_preview_input_cost:.4f} ({total_preview_input_tokens:,} tokens)" ) - if full_documentation: + + if full_documentation.strip(): with st.expander("Full Documentation", expanded=True): - full_documentation = clean_markdown_content(full_documentation) st.markdown(full_documentation) + elif generate: + st.info("No full documentation could be compiled.") - if generate or ( - not preview - and any(section in st.session_state.outputs for section in selected_sections) - ): + + if full_documentation.strip(): add_download_button(selected_sections) @@ -139,7 +185,14 @@ def run_generation(section: str): state.repo_metadata, preview=True, ) - if preview_output.messages and preview_output.messages[0]["content"].strip(): + has_context = preview_output and \ + hasattr(preview_output, 'messages') and \ + isinstance(preview_output.messages, list) and \ + len(preview_output.messages) > 0 and \ + preview_output.messages[0].get("content") is not None and \ + preview_output.messages[0]["content"].strip() != "" + + if has_context: output = generate_docs_section( state.llm_client, section, @@ -150,114 +203,145 @@ def run_generation(section: str): st.session_state.outputs[section] = output state.write_output( { - "content": output.response["content"], - "cost": output.cost, - "tokens": output.tokens, - "messages": output.messages, + "content": getattr(getattr(output, 'response', None), 'content', None), + "cost": getattr(output, 'cost', {}), + "tokens": getattr(output, 'tokens', {}), + "messages": getattr(output, 'messages', []), }, f"{section}.json", ) return output else: st.warning(f"No context found for {section.upper()}. Skipping generation.") - return None # Return None if no generation occurred + st.session_state.outputs.pop(section, None) + return None def run_preview(section: str): - return generate_docs_section( + output = generate_docs_section( state.llm_client, section, state.repo, state.repo_metadata, preview=True, ) + if output: + return output + else: + st.warning(f"Preview generation returned no output for {section.upper()}.") + return None def read_output(section: str): previous_output = state.read_output(f"{section}.json") + + if not previous_output: + st.warning(f"Previous output file found but was empty or invalid for {section.upper()}.") + raise FileNotFoundError(f"Invalid or empty previous output for {section}") + output = type( "Output", (), { - "response": {"content": previous_output["content"]}, - "cost": previous_output["cost"], - "tokens": previous_output["tokens"], + "response": {"content": previous_output.get("content", "")}, + "cost": previous_output.get("cost", {}), + "tokens": previous_output.get("tokens", {}), "messages": previous_output.get( "messages", - [{"content": "Context was not stored with previous output"}], - ), # Use stored messages if available + [{"content": "Context was not stored with previous output"}] if previous_output.get("content") else [] + ), }, - ) + )() st.session_state.outputs[section] = output return output def clean_markdown_content(content: str) -> str: - content = content.strip() - if content.startswith("```markdown") and content.endswith("```"): - content = content[11:-3].strip() - return content + if content is None: + return "" + content_str = str(content).strip() + if content_str.startswith("```markdown") and content_str.endswith("```"): + content_str = content_str[11:-3].strip() + return content_str + + +def _display_section_costs(output, generate, preview): + if output and hasattr(output, 'cost') and hasattr(output, 'tokens'): + if preview: + st.info( + f"Input cost: ${output.cost.get('input_cost', 0):.4f} ({output.tokens.get('input_tokens', 0):,} tokens)" + ) + elif generate: + st.info( + f"Total cost: ${output.cost.get('total_cost', 0):.4f} " + f"(input tokens: {output.tokens.get('input_tokens', 0):,}, " + f"output tokens: {output.tokens.get('output_tokens', 0):,})" + ) + +def _display_section_messages(output, section_name): + if output and hasattr(output, 'messages') and isinstance(output.messages, list): + messages = output.messages + if messages: + with st.expander("Messages", expanded=False): + try: + st.json(messages) + except Exception: + st.warning("Could not display messages.") + + if not messages or (len(messages) > 0 and messages[0].get("content") is not None and not messages[0]["content"].strip()): + st.warning(f"No context found for {section_name.upper()}.") + + +def _display_section_content(output, generate): + if generate and output and hasattr(output, 'response') and isinstance(output.response, dict) and output.response.get('content') is not None: + content = output.response['content'] + if content: + cleaned_content = clean_markdown_content(content) + if cleaned_content.strip(): + st.code(cleaned_content, language="markdown") + else: + st.info("Generated content was empty after cleanup.") def display_section( section: str, generate: bool = False, preview: bool = False, - use_previous: bool = False, ): expander_label = ( f"{section.replace('_', ' ').upper()}{' [Preview]' if preview else ''}" ) with st.expander(expander_label, expanded=False): - if preview: - if use_previous: - try: - output = read_output(section) - except FileNotFoundError: - output = run_preview(section) - else: - output = run_preview(section) - if output: - st.info( - f"Input cost: ${output.cost['input_cost']:.4f} ({output.tokens['input_tokens']:,} tokens)" - ) - elif generate: - output = st.session_state.outputs.get(section) - if output: - st.info( - f"Total cost: ${output.cost['total_cost']:.4f} " - f"(input tokens: {output.tokens['input_tokens']:,}, " - f"output tokens: {output.tokens['output_tokens']:,})" - ) - if output: - with st.expander("Messages", expanded=False): - st.json(output.messages) + output = st.session_state.outputs.get(section) - if not output.messages or not output.messages[0]["content"].strip(): - st.warning(f"No context found for {section.upper()}.") + if output is None: + if generate or preview: + st.info(f"No output available for {section.replace('_', ' ').upper()}.") + return - if generate: - if output: - content = output.response["content"] - content = clean_markdown_content(content) - st.code(content, language="markdown") + _display_section_costs(output, generate, preview) + _display_section_messages(output, section) + _display_section_content(output, generate) def add_download_button(selected_sections: list[str]): - combined_content = "\n\n".join( - [ - clean_markdown_content( - st.session_state.outputs[section].response["content"] - ) - for section in selected_sections - if section in st.session_state.outputs - ] - ) - - st.download_button( - label="Download docs", - data=combined_content, - file_name="docs.md", - mime="text/markdown", - type="primary", - ) + section_contents = [] + for section in selected_sections: + output = st.session_state.outputs.get(section) + if output and hasattr(output, 'response') and isinstance(output.response, dict): + content = output.response.get('content') + if content is not None: + cleaned = clean_markdown_content(content) + if cleaned.strip(): + section_contents.append(cleaned) + + combined_content = "\n\n".join(section_contents) + + if combined_content.strip(): + st.download_button( + label="Download docs", + data=combined_content, + file_name="docs.md", + mime="text/markdown", + type="primary", + ) \ No newline at end of file From b8d7c5a5d5943d708da2b9bb5d6bf5672530208e Mon Sep 17 00:00:00 2001 From: DivergAgent Date: Mon, 7 Jul 2025 15:33:43 +0200 Subject: [PATCH 2/4] Fix 1 SonarQube issues --- src/codeas/ui/components/documentation_ui.py | 338 ++++++++----------- 1 file changed, 144 insertions(+), 194 deletions(-) diff --git a/src/codeas/ui/components/documentation_ui.py b/src/codeas/ui/components/documentation_ui.py index ed4f24c..9f3cf8a 100644 --- a/src/codeas/ui/components/documentation_ui.py +++ b/src/codeas/ui/components/documentation_ui.py @@ -6,21 +6,17 @@ def display(): - # Create a list of documentation sections from SECTION_CONFIG doc_sections = list(SECTION_CONFIG.keys()) - # Format the section names formatted_sections = [ f"{' '.join(section.split('_')).upper()}" for section in doc_sections ] - # Create a dictionary for the data editor doc_data = { - "Incl.": [True] * len(doc_sections), # Default all to True + "Incl.": [True] * len(doc_sections), "Section": formatted_sections, } - # Display the data editor edited_data = st.data_editor( doc_data, column_config={ @@ -31,7 +27,6 @@ def display(): key="doc_sections_editor", ) - # Get the selected sections selected_sections = [ section for section, incl in zip(doc_sections, edited_data["Incl."]) if incl ] @@ -44,6 +39,7 @@ def display(): "Generate documentation", type="primary", key="generate_docs" ) preview_docs = st.button("Preview", key="preview_docs") + if generate_docs: process_sections( selected_sections, generate=True, use_previous=use_previous_outputs @@ -60,123 +56,147 @@ def process_sections( preview: bool = False, use_previous: bool = False, ): - total_generate_cost = 0 - total_generate_input_tokens = 0 - total_generate_output_tokens = 0 + total_gen_cost = 0 + total_gen_input_tokens = 0 + total_gen_output_tokens = 0 total_preview_input_cost = 0 total_preview_input_tokens = 0 - - if 'outputs' not in st.session_state: - st.session_state.outputs = {} - full_documentation = "" with st.expander( f"Sections {'[Preview]' if preview else ''}", expanded=not use_previous ): for section in selected_sections: - output = None with st.spinner( - f"{'Generating' if generate else 'Previewing' if preview else 'Displaying'} {section.replace('_', ' ').upper()}..." + f"{'Generating' if generate else 'Previewing' if preview else 'Displaying'} {section}..." ): + section_output = None + if generate: if use_previous: try: - output = read_output(section) + section_output = read_output(section) except FileNotFoundError: - try: - output = run_generation(section) - except Exception as e: - st.error(f"Error generating {section.replace('_', ' ').upper()}: {e}") - output = None - st.session_state.outputs.pop(section, None) + section_output = run_generation(section) else: - try: - output = run_generation(section) - except Exception as e: - st.error(f"Error generating {section.replace('_', ' ').upper()}: {e}") - output = None - st.session_state.outputs.pop(section, None) + section_output = run_generation(section) - - if output and hasattr(output, 'cost') and hasattr(output, 'tokens'): - total_generate_cost += output.cost.get("total_cost", 0) - total_generate_input_tokens += output.tokens.get("input_tokens", 0) - total_generate_output_tokens += output.tokens.get("output_tokens", 0) + if section_output: + total_gen_cost += section_output.cost.get("total_cost", 0) + total_gen_input_tokens += section_output.tokens.get("input_tokens", 0) + total_gen_output_tokens += section_output.tokens.get("output_tokens", 0) elif preview: if use_previous: try: - output = read_output(section) - if output and hasattr(output, 'cost') and hasattr(output, 'tokens'): - total_preview_input_cost += output.cost.get("input_cost", 0) - total_preview_input_tokens += output.tokens.get("input_tokens", 0) + section_output = read_output(section) except FileNotFoundError: - try: - output = run_preview(section) - if output: - st.session_state.outputs[section] = output - if output and hasattr(output, 'cost') and hasattr(output, 'tokens'): - total_preview_input_cost += output.cost.get("input_cost", 0) - total_preview_input_tokens += output.tokens.get("input_tokens", 0) - except Exception as e: - st.error(f"Error previewing {section.replace('_', ' ').upper()}: {e}") - output = None - st.session_state.outputs.pop(section, None) - + section_output = run_preview(section) else: - try: - output = run_preview(section) - if output: - st.session_state.outputs[section] = output - if output and hasattr(output, 'cost') and hasattr(output, 'tokens'): - total_preview_input_cost += output.cost.get("input_cost", 0) - total_preview_input_tokens += output.tokens.get("input_tokens", 0) - except Exception as e: - st.error(f"Error previewing {section.replace('_', ' ').upper()}: {e}") - output = None - st.session_state.outputs.pop(section, None) - - - display_section(section, generate, preview) - - if generate and section in st.session_state.outputs: - output_for_doc = st.session_state.outputs[section] - content_for_doc = getattr(getattr(output_for_doc, 'response', None), 'content', None) - if content_for_doc: - cleaned_content = clean_markdown_content(content_for_doc) - full_documentation += f"\n\n{cleaned_content}" - + section_output = run_preview(section) + + if section_output: + total_preview_input_cost += section_output.cost.get("input_cost", 0) + total_preview_input_tokens += section_output.tokens.get("input_tokens", 0) + + display_section( + section, + section_output, + generate=generate, + preview=preview, + ) + + if not preview and section in st.session_state.outputs: + output_obj = st.session_state.outputs.get(section) + if output_obj and output_obj.response and "content" in output_obj.response: + cleaned_content = clean_markdown_content( + output_obj.response["content"] + ) + full_documentation += f"\n\n{cleaned_content}" if generate: st.info( - f"Total cost: ${total_generate_cost:.4f} " - f"(input tokens: {total_generate_input_tokens:,}, " - f"output tokens: {total_generate_output_tokens:,})" + f"Total generation cost: ${total_gen_cost:.4f} " + f"(input tokens: {total_gen_input_tokens:,}, " + f"output tokens: {total_gen_output_tokens:,})" ) - if not use_previous: - if total_generate_cost > 0: - usage_tracker.record_usage("generate_docs", total_generate_cost) - else: - st.warning("No generation cost recorded as all sections failed or were skipped.") + if not use_previous and total_gen_cost > 0: + usage_tracker.record_usage("generate_docs", total_gen_cost) elif preview: st.info( - f"Total input cost: ${total_preview_input_cost:.4f} ({total_preview_input_tokens:,} tokens)" + f"Total preview input cost: ${total_preview_input_cost:.4f} ({total_preview_input_tokens:,} tokens)" ) - if full_documentation.strip(): with st.expander("Full Documentation", expanded=True): st.markdown(full_documentation) - elif generate: - st.info("No full documentation could be compiled.") - - if full_documentation.strip(): + if generate or ( + not preview + and any(section in st.session_state.outputs for section in selected_sections) + ): add_download_button(selected_sections) +def _display_cost_info(output: object | None, generate: bool, preview: bool): + if output: + if preview: + input_cost = output.cost.get('input_cost', 0) + input_tokens = output.tokens.get('input_tokens', 0) + st.info( + f"Input cost: ${input_cost:.4f} ({input_tokens:,} tokens)" + ) + elif generate: + total_cost = output.cost.get('total_cost', 0) + input_tokens = output.tokens.get('input_tokens', 0) + output_tokens = output.tokens.get('output_tokens', 0) + st.info( + f"Section cost: ${total_cost:.4f} " + f"(input tokens: {input_tokens:,}, " + f"output tokens: {output_tokens:,})", + help="Cost/tokens for this specific section generation." + ) + + +def _display_messages_and_warnings(output: object | None, section: str): + if output: + if output.messages: + with st.expander("Messages", expanded=False): + st.json(output.messages) + + if not output.messages or (output.messages and len(output.messages) > 0 and "content" in output.messages[0] and not output.messages[0]["content"].strip()): + st.warning(f"No context found or empty context message for {section.upper()}.") + + +def _display_generated_content(output: object | None): + if output: + if output.response and "content" in output.response: + content = output.response["content"] + content = clean_markdown_content(content) + st.code(content, language="markdown") + else: + st.warning("No generated content available for display for this section.") + + +def display_section( + section: str, + output: object | None, + generate: bool = False, + preview: bool = False, +): + expander_label = ( + f"{section.replace('_', ' ').upper()}{' [Preview]' if preview else ''}" + ) + + with st.expander(expander_label, expanded=False): + _display_cost_info(output, generate, preview) + _display_messages_and_warnings(output, section) + + if generate: + _display_generated_content(output) + + def run_generation(section: str): preview_output = generate_docs_section( state.llm_client, @@ -185,14 +205,7 @@ def run_generation(section: str): state.repo_metadata, preview=True, ) - has_context = preview_output and \ - hasattr(preview_output, 'messages') and \ - isinstance(preview_output.messages, list) and \ - len(preview_output.messages) > 0 and \ - preview_output.messages[0].get("content") is not None and \ - preview_output.messages[0]["content"].strip() != "" - - if has_context: + if preview_output and hasattr(preview_output, 'messages') and preview_output.messages and len(preview_output.messages) > 0 and "content" in preview_output.messages[0] and preview_output.messages[0]["content"].strip(): output = generate_docs_section( state.llm_client, section, @@ -203,139 +216,76 @@ def run_generation(section: str): st.session_state.outputs[section] = output state.write_output( { - "content": getattr(getattr(output, 'response', None), 'content', None), - "cost": getattr(output, 'cost', {}), - "tokens": getattr(output, 'tokens', {}), - "messages": getattr(output, 'messages', []), + "content": output.response["content"], + "cost": output.cost, + "tokens": output.tokens, + "messages": output.messages, }, f"{section}.json", ) return output else: st.warning(f"No context found for {section.upper()}. Skipping generation.") - st.session_state.outputs.pop(section, None) return None def run_preview(section: str): - output = generate_docs_section( + return generate_docs_section( state.llm_client, section, state.repo, state.repo_metadata, preview=True, ) - if output: - return output - else: - st.warning(f"Preview generation returned no output for {section.upper()}.") - return None def read_output(section: str): previous_output = state.read_output(f"{section}.json") - if not previous_output: - st.warning(f"Previous output file found but was empty or invalid for {section.upper()}.") - raise FileNotFoundError(f"Invalid or empty previous output for {section}") - - output = type( - "Output", - (), - { - "response": {"content": previous_output.get("content", "")}, - "cost": previous_output.get("cost", {}), - "tokens": previous_output.get("tokens", {}), - "messages": previous_output.get( - "messages", - [{"content": "Context was not stored with previous output"}] if previous_output.get("content") else [] - ), - }, - )() - st.session_state.outputs[section] = output - return output - - -def clean_markdown_content(content: str) -> str: - if content is None: - return "" - content_str = str(content).strip() - if content_str.startswith("```markdown") and content_str.endswith("```"): - content_str = content_str[11:-3].strip() - return content_str - - -def _display_section_costs(output, generate, preview): - if output and hasattr(output, 'cost') and hasattr(output, 'tokens'): - if preview: - st.info( - f"Input cost: ${output.cost.get('input_cost', 0):.4f} ({output.tokens.get('input_tokens', 0):,} tokens)" - ) - elif generate: - st.info( - f"Total cost: ${output.cost.get('total_cost', 0):.4f} " - f"(input tokens: {output.tokens.get('input_tokens', 0):,}, " - f"output tokens: {output.tokens.get('output_tokens', 0):,})" - ) - -def _display_section_messages(output, section_name): - if output and hasattr(output, 'messages') and isinstance(output.messages, list): - messages = output.messages - if messages: - with st.expander("Messages", expanded=False): - try: - st.json(messages) - except Exception: - st.warning("Could not display messages.") + output_data = { + "response": {"content": previous_output.get("content", "")}, + "cost": previous_output.get("cost", {"total_cost": 0, "input_cost": 0}), + "tokens": previous_output.get("tokens", {"input_tokens": 0, "output_tokens": 0}), + "messages": previous_output.get( + "messages", + [{"role": "system", "content": "Context was not stored with previous output"}], + ), + } - if not messages or (len(messages) > 0 and messages[0].get("content") is not None and not messages[0]["content"].strip()): - st.warning(f"No context found for {section_name.upper()}.") + class OutputObject: + def __init__(self, data): + self.response = data["response"] + self.cost = data["cost"] + self.tokens = data["tokens"] + self.messages = data["messages"] + def __getattr__(self, name): + if name in self.__dict__: + return self.__dict__[name] + return None -def _display_section_content(output, generate): - if generate and output and hasattr(output, 'response') and isinstance(output.response, dict) and output.response.get('content') is not None: - content = output.response['content'] - if content: - cleaned_content = clean_markdown_content(content) - if cleaned_content.strip(): - st.code(cleaned_content, language="markdown") - else: - st.info("Generated content was empty after cleanup.") + output = OutputObject(output_data) + st.session_state.outputs[section] = output -def display_section( - section: str, - generate: bool = False, - preview: bool = False, -): - expander_label = ( - f"{section.replace('_', ' ').upper()}{' [Preview]' if preview else ''}" - ) - with st.expander(expander_label, expanded=False): - output = st.session_state.outputs.get(section) + return output - if output is None: - if generate or preview: - st.info(f"No output available for {section.replace('_', ' ').upper()}.") - return - _display_section_costs(output, generate, preview) - _display_section_messages(output, section) - _display_section_content(output, generate) +def clean_markdown_content(content: str) -> str: + content = content.strip() + if content.startswith("```markdown") and content.endswith("```"): + content = content[11:-3].strip() + return content def add_download_button(selected_sections: list[str]): - section_contents = [] + combined_content_parts = [] for section in selected_sections: - output = st.session_state.outputs.get(section) - if output and hasattr(output, 'response') and isinstance(output.response, dict): - content = output.response.get('content') - if content is not None: - cleaned = clean_markdown_content(content) - if cleaned.strip(): - section_contents.append(cleaned) - - combined_content = "\n\n".join(section_contents) + output_obj = st.session_state.outputs.get(section) + if output_obj and hasattr(output_obj, 'response') and output_obj.response and "content" in output_obj.response: + combined_content_parts.append(clean_markdown_content(output_obj.response["content"])) + + combined_content = "\n\n".join(combined_content_parts) if combined_content.strip(): st.download_button( From eef108796c05b1e071aebbdc1bddb458e5aeb8de Mon Sep 17 00:00:00 2001 From: DivergAgent Date: Mon, 7 Jul 2025 15:51:49 +0200 Subject: [PATCH 3/4] Fix 1 SonarQube issues --- src/codeas/ui/components/documentation_ui.py | 347 ++++++++++--------- 1 file changed, 191 insertions(+), 156 deletions(-) diff --git a/src/codeas/ui/components/documentation_ui.py b/src/codeas/ui/components/documentation_ui.py index 9f3cf8a..2283ec1 100644 --- a/src/codeas/ui/components/documentation_ui.py +++ b/src/codeas/ui/components/documentation_ui.py @@ -17,7 +17,7 @@ def display(): "Section": formatted_sections, } - edited_data = st.data_editor( + st.data_editor( doc_data, column_config={ "Incl.": st.column_config.CheckboxColumn(width="small"), @@ -28,7 +28,7 @@ def display(): ) selected_sections = [ - section for section, incl in zip(doc_sections, edited_data["Incl."]) if incl + section for section, incl in zip(doc_sections, st.session_state["doc_sections_editor"]["Incl."]) if incl ] use_previous_outputs = st.toggle( @@ -39,7 +39,6 @@ def display(): "Generate documentation", type="primary", key="generate_docs" ) preview_docs = st.button("Preview", key="preview_docs") - if generate_docs: process_sections( selected_sections, generate=True, use_previous=use_previous_outputs @@ -56,11 +55,11 @@ def process_sections( preview: bool = False, use_previous: bool = False, ): - total_gen_cost = 0 - total_gen_input_tokens = 0 - total_gen_output_tokens = 0 - total_preview_input_cost = 0 - total_preview_input_tokens = 0 + total_cost = 0 + total_input_tokens = 0 + total_output_tokens = 0 + total_input_cost = 0 + total_input_tokens = 0 full_documentation = "" with st.expander( @@ -70,131 +69,76 @@ def process_sections( with st.spinner( f"{'Generating' if generate else 'Previewing' if preview else 'Displaying'} {section}..." ): - section_output = None - + output = None if generate: if use_previous: try: - section_output = read_output(section) + output = read_output(section) except FileNotFoundError: - section_output = run_generation(section) + output = run_generation(section) else: - section_output = run_generation(section) - - if section_output: - total_gen_cost += section_output.cost.get("total_cost", 0) - total_gen_input_tokens += section_output.tokens.get("input_tokens", 0) - total_gen_output_tokens += section_output.tokens.get("output_tokens", 0) - + output = run_generation(section) + if output: + if hasattr(output, 'cost') and isinstance(output.cost, dict) and 'total_cost' in output.cost: + total_cost += output.cost["total_cost"] + if hasattr(output, 'tokens') and isinstance(output.tokens, dict): + if 'input_tokens' in output.tokens: + total_input_tokens += output.tokens["input_tokens"] + if 'output_tokens' in output.tokens: + total_output_tokens += output.tokens["output_tokens"] elif preview: if use_previous: try: - section_output = read_output(section) + output = read_output(section) except FileNotFoundError: - section_output = run_preview(section) + output = run_preview(section) else: - section_output = run_preview(section) - - if section_output: - total_preview_input_cost += section_output.cost.get("input_cost", 0) - total_preview_input_tokens += section_output.tokens.get("input_tokens", 0) - - display_section( - section, - section_output, - generate=generate, - preview=preview, - ) - - if not preview and section in st.session_state.outputs: - output_obj = st.session_state.outputs.get(section) - if output_obj and output_obj.response and "content" in output_obj.response: - cleaned_content = clean_markdown_content( - output_obj.response["content"] - ) - full_documentation += f"\n\n{cleaned_content}" + output = run_preview(section) + if output: + if hasattr(output, 'cost') and isinstance(output.cost, dict) and 'input_cost' in output.cost: + total_input_cost += output.cost["input_cost"] + if hasattr(output, 'tokens') and isinstance(output.tokens, dict) and 'input_tokens' in output.tokens: + total_input_tokens += output.tokens["input_tokens"] + + display_section(section, generate, preview, section_output=output) + + if not preview and section in st.session_state.outputs and \ + hasattr(st.session_state.outputs[section], 'response') and \ + isinstance(st.session_state.outputs[section].response, dict) and \ + 'content' in st.session_state.outputs[section].response: + cleaned_content = clean_markdown_content( + st.session_state.outputs[section].response["content"] + ) + full_documentation += f"\n\n{cleaned_content}" if generate: st.info( - f"Total generation cost: ${total_gen_cost:.4f} " - f"(input tokens: {total_gen_input_tokens:,}, " - f"output tokens: {total_gen_output_tokens:,})" + f"Total cost: ${total_cost:.4f} " + f"(input tokens: {total_input_tokens:,}, " + f"output tokens: {total_output_tokens:,})" ) - if not use_previous and total_gen_cost > 0: - usage_tracker.record_usage("generate_docs", total_gen_cost) - + if not use_previous: + usage_tracker.record_usage("generate_docs", total_cost) elif preview: st.info( - f"Total preview input cost: ${total_preview_input_cost:.4f} ({total_preview_input_tokens:,} tokens)" + f"Total input cost: ${total_input_cost:.4f} ({total_input_tokens:,} tokens)" ) - if full_documentation.strip(): + if generate and full_documentation.strip(): with st.expander("Full Documentation", expanded=True): + full_documentation = clean_markdown_content(full_documentation) st.markdown(full_documentation) + elif generate and not full_documentation.strip() and selected_sections: + st.info("No documentation generated for the selected sections.") + if generate or ( not preview and any(section in st.session_state.outputs for section in selected_sections) ): add_download_button(selected_sections) - - -def _display_cost_info(output: object | None, generate: bool, preview: bool): - if output: - if preview: - input_cost = output.cost.get('input_cost', 0) - input_tokens = output.tokens.get('input_tokens', 0) - st.info( - f"Input cost: ${input_cost:.4f} ({input_tokens:,} tokens)" - ) - elif generate: - total_cost = output.cost.get('total_cost', 0) - input_tokens = output.tokens.get('input_tokens', 0) - output_tokens = output.tokens.get('output_tokens', 0) - st.info( - f"Section cost: ${total_cost:.4f} " - f"(input tokens: {input_tokens:,}, " - f"output tokens: {output_tokens:,})", - help="Cost/tokens for this specific section generation." - ) - - -def _display_messages_and_warnings(output: object | None, section: str): - if output: - if output.messages: - with st.expander("Messages", expanded=False): - st.json(output.messages) - - if not output.messages or (output.messages and len(output.messages) > 0 and "content" in output.messages[0] and not output.messages[0]["content"].strip()): - st.warning(f"No context found or empty context message for {section.upper()}.") - - -def _display_generated_content(output: object | None): - if output: - if output.response and "content" in output.response: - content = output.response["content"] - content = clean_markdown_content(content) - st.code(content, language="markdown") - else: - st.warning("No generated content available for display for this section.") - - -def display_section( - section: str, - output: object | None, - generate: bool = False, - preview: bool = False, -): - expander_label = ( - f"{section.replace('_', ' ').upper()}{' [Preview]' if preview else ''}" - ) - - with st.expander(expander_label, expanded=False): - _display_cost_info(output, generate, preview) - _display_messages_and_warnings(output, section) - - if generate: - _display_generated_content(output) + elif not generate and not preview and selected_sections: + st.info("Select 'Generate documentation' to create content or 'Preview' to see context.") def run_generation(section: str): @@ -205,7 +149,10 @@ def run_generation(section: str): state.repo_metadata, preview=True, ) - if preview_output and hasattr(preview_output, 'messages') and preview_output.messages and len(preview_output.messages) > 0 and "content" in preview_output.messages[0] and preview_output.messages[0]["content"].strip(): + if preview_output and hasattr(preview_output, 'messages') and isinstance(preview_output.messages, list) and \ + len(preview_output.messages) > 0 and isinstance(preview_output.messages[0], dict) and \ + preview_output.messages[0].get("content", "").strip(): + output = generate_docs_section( state.llm_client, section, @@ -214,18 +161,26 @@ def run_generation(section: str): preview=False, ) st.session_state.outputs[section] = output - state.write_output( - { - "content": output.response["content"], - "cost": output.cost, - "tokens": output.tokens, - "messages": output.messages, - }, - f"{section}.json", - ) - return output + if hasattr(output, 'response') and hasattr(output.response, 'content') and \ + hasattr(output, 'cost') and hasattr(output, 'tokens') and hasattr(output, 'messages'): + try: + state.write_output( + { + "content": output.response["content"], + "cost": output.cost, + "tokens": output.tokens, + "messages": output.messages, + }, + f"{section}.json", + ) + except Exception as e: + st.error(f"Error writing output for {section.upper()}: {e}") + return output + else: + st.warning(f"Generated output for {section.upper()} is missing expected attributes (response, cost, tokens, messages). Not writing to file.") + return output else: - st.warning(f"No context found for {section.upper()}. Skipping generation.") + st.warning(f"No context found or preview failed for {section.upper()}. Skipping generation.") return None @@ -240,52 +195,130 @@ def run_preview(section: str): def read_output(section: str): - previous_output = state.read_output(f"{section}.json") - - output_data = { - "response": {"content": previous_output.get("content", "")}, - "cost": previous_output.get("cost", {"total_cost": 0, "input_cost": 0}), - "tokens": previous_output.get("tokens", {"input_tokens": 0, "output_tokens": 0}), - "messages": previous_output.get( - "messages", - [{"role": "system", "content": "Context was not stored with previous output"}], - ), - } + try: + previous_output = state.read_output(f"{section}.json") + if not isinstance(previous_output, dict): + st.error(f"Previous output for {section} is not a dictionary.") + raise FileNotFoundError(f"Invalid data format for {section}.json") + + if "content" not in previous_output or "cost" not in previous_output or "tokens" not in previous_output: + st.error(f"Previous output for {section} is missing required keys.") + raise FileNotFoundError(f"Missing data in {section}.json") + + output = type( + "Output", + (), + { + "response": {"content": previous_output["content"]}, + "cost": previous_output["cost"], + "tokens": previous_output["tokens"], + "messages": previous_output.get( + "messages", + [{"content": "Context was not stored with previous output"}], + ), + }, + )() + st.session_state.outputs[section] = output + return output + except FileNotFoundError: + raise + except Exception as e: + st.error(f"Error reading previous output for {section}: {e}") + raise + - class OutputObject: - def __init__(self, data): - self.response = data["response"] - self.cost = data["cost"] - self.tokens = data["tokens"] - self.messages = data["messages"] - def __getattr__(self, name): - if name in self.__dict__: - return self.__dict__[name] - return None +def clean_markdown_content(content: str) -> str: + if not isinstance(content, str): + return "" + content = content.strip() + if content.startswith("```markdown") and content.endswith("```"): + content = content[len("```markdown"):-len("```")].strip() + return content - output = OutputObject(output_data) +def _display_section_cost(output, generate: bool, preview: bool): + if preview: + input_cost = output.cost.get('input_cost') if hasattr(output, 'cost') and isinstance(output.cost, dict) else None + input_tokens = output.tokens.get('input_tokens') if hasattr(output, 'tokens') and isinstance(output.tokens, dict) else None + if input_cost is not None and input_tokens is not None: + st.info( + f"Input cost: ${input_cost:.4f} ({input_tokens:,} tokens)" + ) + else: + st.info("Input cost information not available.") + elif generate: + total_cost = output.cost.get('total_cost') if hasattr(output, 'cost') and isinstance(output.cost, dict) else None + input_tokens = output.tokens.get('input_tokens') if hasattr(output, 'tokens') and isinstance(output.tokens, dict) else None + output_tokens = output.tokens.get('output_tokens') if hasattr(output, 'tokens') and isinstance(output.tokens, dict) else None + if total_cost is not None and input_tokens is not None and output_tokens is not None: + st.info( + f"Total cost: ${total_cost:.4f} " + f"(input tokens: {input_tokens:,}, " + f"output tokens: {output_tokens:,})") + else: + st.info("Total cost information not available.") - st.session_state.outputs[section] = output - return output +def _display_section_messages_and_context(output, section: str): + with st.expander("Messages", expanded=False): + messages = getattr(output, 'messages', None) + if messages and isinstance(messages, list): + st.json(messages) + else: + st.info("No messages available.") + messages = getattr(output, 'messages', None) + first_message_content = "" + if messages and isinstance(messages, list) and len(messages) > 0 and isinstance(messages[0], dict): + first_message_content = messages[0].get("content", "") -def clean_markdown_content(content: str) -> str: - content = content.strip() - if content.startswith("```markdown") and content.endswith("```"): - content = content[11:-3].strip() - return content + if not messages or not first_message_content.strip(): + st.warning(f"No context or meaningful response found for {section.upper()}.") -def add_download_button(selected_sections: list[str]): - combined_content_parts = [] - for section in selected_sections: - output_obj = st.session_state.outputs.get(section) - if output_obj and hasattr(output_obj, 'response') and output_obj.response and "content" in output_obj.response: - combined_content_parts.append(clean_markdown_content(output_obj.response["content"])) +def _display_section_content(output, generate: bool): + if generate and hasattr(output, 'response') and isinstance(output.response, dict) and 'content' in output.response: + content = output.response["content"] + content = clean_markdown_content(content) + st.code(content, language="markdown") + elif generate: + st.info("Generated content is not available in the expected format.") - combined_content = "\n\n".join(combined_content_parts) + +def display_section( + section: str, + generate: bool = False, + preview: bool = False, + section_output=None, +): + expander_label = ( + f"{section.replace('_', ' ').upper()}{' [Preview]' if preview else ''}" + ) + + output = section_output + + with st.expander(expander_label, expanded=False): + if output: + _display_section_cost(output, generate, preview) + _display_section_messages_and_context(output, section) + _display_section_content(output, generate) + else: + st.info(f"No output available for {section.upper()}.") + + +def add_download_button(selected_sections: list[str]): + combined_content = "\n\n".join( + [ + clean_markdown_content( + st.session_state.outputs[section].response["content"] + ) + for section in selected_sections + if section in st.session_state.outputs and \ + hasattr(st.session_state.outputs[section], 'response') and \ + isinstance(st.session_state.outputs[section].response, dict) and \ + 'content' in st.session_state.outputs[section].response + ] + ) if combined_content.strip(): st.download_button( @@ -294,4 +327,6 @@ def add_download_button(selected_sections: list[str]): file_name="docs.md", mime="text/markdown", type="primary", - ) \ No newline at end of file + ) + else: + st.info("Generate some documentation to enable the download button.") \ No newline at end of file From 6684976fa7ddbf642e45a1a0f48729fb1fbaeee9 Mon Sep 17 00:00:00 2001 From: DivergAgent Date: Mon, 7 Jul 2025 16:07:18 +0200 Subject: [PATCH 4/4] Fix 1 SonarQube issues --- src/codeas/ui/components/documentation_ui.py | 253 ++++++++----------- 1 file changed, 109 insertions(+), 144 deletions(-) diff --git a/src/codeas/ui/components/documentation_ui.py b/src/codeas/ui/components/documentation_ui.py index 2283ec1..bc7a431 100644 --- a/src/codeas/ui/components/documentation_ui.py +++ b/src/codeas/ui/components/documentation_ui.py @@ -17,7 +17,7 @@ def display(): "Section": formatted_sections, } - st.data_editor( + edited_data = st.data_editor( doc_data, column_config={ "Incl.": st.column_config.CheckboxColumn(width="small"), @@ -28,7 +28,7 @@ def display(): ) selected_sections = [ - section for section, incl in zip(doc_sections, st.session_state["doc_sections_editor"]["Incl."]) if incl + section for section, incl in zip(doc_sections, edited_data["Incl."]) if incl ] use_previous_outputs = st.toggle( @@ -56,20 +56,20 @@ def process_sections( use_previous: bool = False, ): total_cost = 0 - total_input_tokens = 0 total_output_tokens = 0 total_input_cost = 0 total_input_tokens = 0 + full_documentation = "" with st.expander( f"Sections {'[Preview]' if preview else ''}", expanded=not use_previous ): for section in selected_sections: + output = None with st.spinner( f"{'Generating' if generate else 'Previewing' if preview else 'Displaying'} {section}..." ): - output = None if generate: if use_previous: try: @@ -78,14 +78,16 @@ def process_sections( output = run_generation(section) else: output = run_generation(section) + if output: if hasattr(output, 'cost') and isinstance(output.cost, dict) and 'total_cost' in output.cost: - total_cost += output.cost["total_cost"] - if hasattr(output, 'tokens') and isinstance(output.tokens, dict): - if 'input_tokens' in output.tokens: - total_input_tokens += output.tokens["input_tokens"] - if 'output_tokens' in output.tokens: - total_output_tokens += output.tokens["output_tokens"] + total_cost += output.cost["total_cost"] + if hasattr(output, 'tokens') and isinstance(output.tokens, dict) and 'input_tokens' in output.tokens: + total_input_tokens += output.tokens["input_tokens"] + if hasattr(output, 'tokens') and isinstance(output.tokens, dict) and 'output_tokens' in output.tokens: + total_output_tokens += output.tokens["output_tokens"] + + elif preview: if use_previous: try: @@ -94,22 +96,25 @@ def process_sections( output = run_preview(section) else: output = run_preview(section) + if output: - if hasattr(output, 'cost') and isinstance(output.cost, dict) and 'input_cost' in output.cost: - total_input_cost += output.cost["input_cost"] - if hasattr(output, 'tokens') and isinstance(output.tokens, dict) and 'input_tokens' in output.tokens: - total_input_tokens += output.tokens["input_tokens"] + st.session_state.outputs[section] = output - display_section(section, generate, preview, section_output=output) + if output and hasattr(output, 'cost') and isinstance(output.cost, dict) and 'input_cost' in output.cost: + total_input_cost += output.cost["input_cost"] + if output and hasattr(output, 'tokens') and isinstance(output.tokens, dict) and 'input_tokens' in output.tokens: + total_input_tokens += output.tokens["input_tokens"] + + display_section(section, generate=generate, preview=preview) + + if not preview and section in st.session_state.outputs: + current_output = st.session_state.outputs[section] + if hasattr(current_output, 'response') and isinstance(current_output.response, dict) and 'content' in current_output.response: + cleaned_content = clean_markdown_content( + current_output.response["content"] + ) + full_documentation += f"\n\n{cleaned_content}" - if not preview and section in st.session_state.outputs and \ - hasattr(st.session_state.outputs[section], 'response') and \ - isinstance(st.session_state.outputs[section].response, dict) and \ - 'content' in st.session_state.outputs[section].response: - cleaned_content = clean_markdown_content( - st.session_state.outputs[section].response["content"] - ) - full_documentation += f"\n\n{cleaned_content}" if generate: st.info( @@ -124,21 +129,15 @@ def process_sections( f"Total input cost: ${total_input_cost:.4f} ({total_input_tokens:,} tokens)" ) - if generate and full_documentation.strip(): + if full_documentation: with st.expander("Full Documentation", expanded=True): - full_documentation = clean_markdown_content(full_documentation) st.markdown(full_documentation) - elif generate and not full_documentation.strip() and selected_sections: - st.info("No documentation generated for the selected sections.") - if generate or ( not preview and any(section in st.session_state.outputs for section in selected_sections) ): add_download_button(selected_sections) - elif not generate and not preview and selected_sections: - st.info("Select 'Generate documentation' to create content or 'Preview' to see context.") def run_generation(section: str): @@ -149,10 +148,7 @@ def run_generation(section: str): state.repo_metadata, preview=True, ) - if preview_output and hasattr(preview_output, 'messages') and isinstance(preview_output.messages, list) and \ - len(preview_output.messages) > 0 and isinstance(preview_output.messages[0], dict) and \ - preview_output.messages[0].get("content", "").strip(): - + if preview_output.messages and preview_output.messages[0]["content"].strip(): output = generate_docs_section( state.llm_client, section, @@ -161,26 +157,18 @@ def run_generation(section: str): preview=False, ) st.session_state.outputs[section] = output - if hasattr(output, 'response') and hasattr(output.response, 'content') and \ - hasattr(output, 'cost') and hasattr(output, 'tokens') and hasattr(output, 'messages'): - try: - state.write_output( - { - "content": output.response["content"], - "cost": output.cost, - "tokens": output.tokens, - "messages": output.messages, - }, - f"{section}.json", - ) - except Exception as e: - st.error(f"Error writing output for {section.upper()}: {e}") - return output - else: - st.warning(f"Generated output for {section.upper()} is missing expected attributes (response, cost, tokens, messages). Not writing to file.") - return output + state.write_output( + { + "content": output.response["content"], + "cost": output.cost, + "tokens": output.tokens, + "messages": output.messages, + }, + f"{section}.json", + ) + return output else: - st.warning(f"No context found or preview failed for {section.upper()}. Skipping generation.") + st.warning(f"No context found for {section.upper()}. Skipping generation.") return None @@ -195,36 +183,22 @@ def run_preview(section: str): def read_output(section: str): - try: - previous_output = state.read_output(f"{section}.json") - if not isinstance(previous_output, dict): - st.error(f"Previous output for {section} is not a dictionary.") - raise FileNotFoundError(f"Invalid data format for {section}.json") - - if "content" not in previous_output or "cost" not in previous_output or "tokens" not in previous_output: - st.error(f"Previous output for {section} is missing required keys.") - raise FileNotFoundError(f"Missing data in {section}.json") - - output = type( - "Output", - (), - { - "response": {"content": previous_output["content"]}, - "cost": previous_output["cost"], - "tokens": previous_output["tokens"], - "messages": previous_output.get( - "messages", - [{"content": "Context was not stored with previous output"}], - ), - }, - )() - st.session_state.outputs[section] = output - return output - except FileNotFoundError: - raise - except Exception as e: - st.error(f"Error reading previous output for {section}: {e}") - raise + previous_output = state.read_output(f"{section}.json") + output = type( + "Output", + (), + { + "response": {"content": previous_output.get("content", "")}, + "cost": previous_output.get("cost", {}), + "tokens": previous_output.get("tokens", {}), + "messages": previous_output.get( + "messages", + [{"content": "Context was not stored with previous output" if previous_output.get("messages") is None else ""}], + ), + }, + )() + st.session_state.outputs[section] = output + return output def clean_markdown_content(content: str) -> str: @@ -232,101 +206,92 @@ def clean_markdown_content(content: str) -> str: return "" content = content.strip() if content.startswith("```markdown") and content.endswith("```"): - content = content[len("```markdown"):-len("```")].strip() + content = content[11:-3].strip() return content -def _display_section_cost(output, generate: bool, preview: bool): - if preview: - input_cost = output.cost.get('input_cost') if hasattr(output, 'cost') and isinstance(output.cost, dict) else None - input_tokens = output.tokens.get('input_tokens') if hasattr(output, 'tokens') and isinstance(output.tokens, dict) else None - if input_cost is not None and input_tokens is not None: - st.info( - f"Input cost: ${input_cost:.4f} ({input_tokens:,} tokens)" - ) - else: - st.info("Input cost information not available.") - elif generate: - total_cost = output.cost.get('total_cost') if hasattr(output, 'cost') and isinstance(output.cost, dict) else None - input_tokens = output.tokens.get('input_tokens') if hasattr(output, 'tokens') and isinstance(output.tokens, dict) else None - output_tokens = output.tokens.get('output_tokens') if hasattr(output, 'tokens') and isinstance(output.tokens, dict) else None - if total_cost is not None and input_tokens is not None and output_tokens is not None: - st.info( - f"Total cost: ${total_cost:.4f} " - f"(input tokens: {input_tokens:,}, " - f"output tokens: {output_tokens:,})") - else: - st.info("Total cost information not available.") - - -def _display_section_messages_and_context(output, section: str): +def _display_cost_info(output, generate, preview): + if preview and hasattr(output, 'cost') and isinstance(output.cost, dict) and 'input_cost' in output.cost and \ + hasattr(output, 'tokens') and isinstance(output.tokens, dict) and 'input_tokens' in output.tokens: + st.info( + f"Input cost: ${output.cost['input_cost']:.4f} ({output.tokens['input_tokens']:,} tokens)" + ) + elif generate and hasattr(output, 'cost') and isinstance(output.cost, dict) and 'total_cost' in output.cost and \ + hasattr(output, 'tokens') and isinstance(output.tokens, dict) and 'input_tokens' in output.tokens and 'output_tokens' in output.tokens: + st.info( + f"Total cost: ${output.cost['total_cost']:.4f} " + f"(input tokens: {output.tokens['input_tokens']:,}, " + f"output tokens: {output.tokens['output_tokens']:,})" + ) + +def _display_valid_messages(messages, section): with st.expander("Messages", expanded=False): - messages = getattr(output, 'messages', None) - if messages and isinstance(messages, list): - st.json(messages) + if all(isinstance(msg, dict) for msg in messages): + st.json(messages) else: - st.info("No messages available.") + st.warning("Messages format unexpected.") + st.write(messages) - messages = getattr(output, 'messages', None) - first_message_content = "" - if messages and isinstance(messages, list) and len(messages) > 0 and isinstance(messages[0], dict): - first_message_content = messages[0].get("content", "") + if isinstance(messages, list) and len(messages) > 0 and \ + isinstance(messages[0], dict) and 'content' in messages[0] and \ + not messages[0]["content"].strip(): + st.warning(f"No context found for {section.upper()}.") - if not messages or not first_message_content.strip(): - st.warning(f"No context or meaningful response found for {section.upper()}.") +def _display_messages(output, section): + if hasattr(output, 'messages') and output.messages and isinstance(output.messages, list): + _display_valid_messages(output.messages, section) + elif hasattr(output, 'messages') and (output.messages is None or (isinstance(output.messages, list) and not output.messages)): + st.info("No messages stored for this section.") -def _display_section_content(output, generate: bool): - if generate and hasattr(output, 'response') and isinstance(output.response, dict) and 'content' in output.response: +def _display_content(output, generate, section): + if generate and hasattr(output, 'response') and isinstance(output.response, dict) and 'content' in output.response: content = output.response["content"] content = clean_markdown_content(content) st.code(content, language="markdown") - elif generate: - st.info("Generated content is not available in the expected format.") + elif generate: + st.warning(f"Could not find content for {section.upper()} in output.") def display_section( section: str, generate: bool = False, preview: bool = False, - section_output=None, ): expander_label = ( f"{section.replace('_', ' ').upper()}{' [Preview]' if preview else ''}" ) - output = section_output + output = st.session_state.outputs.get(section) with st.expander(expander_label, expanded=False): if output: - _display_section_cost(output, generate, preview) - _display_section_messages_and_context(output, section) - _display_section_content(output, generate) - else: - st.info(f"No output available for {section.upper()}.") + _display_cost_info(output, generate, preview) + _display_messages(output, section) + _display_content(output, generate, section) def add_download_button(selected_sections: list[str]): combined_content = "\n\n".join( [ clean_markdown_content( - st.session_state.outputs[section].response["content"] + st.session_state.outputs[section].response["content"] + if section in st.session_state.outputs + and hasattr(st.session_state.outputs[section], 'response') + and isinstance(st.session_state.outputs[section].response, dict) + and "content" in st.session_state.outputs[section].response + else "" ) for section in selected_sections - if section in st.session_state.outputs and \ - hasattr(st.session_state.outputs[section], 'response') and \ - isinstance(st.session_state.outputs[section].response, dict) and \ - 'content' in st.session_state.outputs[section].response + if section in st.session_state.outputs ] ) - if combined_content.strip(): - st.download_button( - label="Download docs", - data=combined_content, - file_name="docs.md", - mime="text/markdown", - type="primary", - ) - else: - st.info("Generate some documentation to enable the download button.") \ No newline at end of file + st.download_button( + label="Download docs", + data=combined_content, + file_name="docs.md", + mime="text/markdown", + type="primary", + disabled=not combined_content.strip() + ) \ No newline at end of file