WIP: adding pygentic.css for css spinner element; patching other components. Result objects not loading correctly and messages not stacking

This commit is contained in:
Francis Secada 2025-01-21 22:36:11 -05:00
parent 07e7fa09a5
commit 05d8baffb1
11 changed files with 85 additions and 38 deletions

View File

@ -1,4 +1,6 @@
AI_MODEL = "gpt-4o" from backend.utils import get_val
AI_MODEL = get_val("OPENAI_MODEL", "gpt-4o")
default_system_prompt = """ default_system_prompt = """
You are an advanced and intelligent AI assistant specializing in generating You are an advanced and intelligent AI assistant specializing in generating
comprehensive and detailed SWOT analyses for a variety of scenarios, topics, comprehensive and detailed SWOT analyses for a variety of scenarios, topics,

View File

@ -1,3 +1,5 @@
from pprint import pformat
import httpx import httpx
from bs4 import BeautifulSoup as soup from bs4 import BeautifulSoup as soup
from pydantic_ai import RunContext from pydantic_ai import RunContext
@ -117,7 +119,7 @@ async def run_agent(
f"Perform a comprehensive SWOT analysis for this product: {url}", f"Perform a comprehensive SWOT analysis for this product: {url}",
deps=deps, deps=deps,
) )
logger.info(f"Agent Result: {result}") logger.info(f"Agent Result: {pformat(result.data)}")
if deps.update_status_func: if deps.update_status_func:
await deps.update_status_func(deps.request, "Analysis Complete") await deps.update_status_func(deps.request, "Analysis Complete")

View File

@ -0,0 +1,31 @@
/* Spinner Wrapper with Overlay Effect */
.spinner-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.7); /* Light overlay effect */
display: flex;
justify-content: center;
align-items: center;
z-index: 50;
}
.loader {
border: 4px solid rgba(0, 0, 0, 0.1); /* Light gray */
border-top: 4px solid #3273dc; /* Bulma primary color */
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -2,6 +2,10 @@
<meta name="viewport" <meta name="viewport"
content="width=device-width, initial-scale=1.0"> content="width=device-width, initial-scale=1.0">
<title>{% block title %}Pygentic AI{% endblock title %}</title> <title>{% block title %}Pygentic AI{% endblock title %}</title>
<link rel="icon"
href="{{ url_for('static', path='favicon.ico') }}"
id="favicon"
type='image/x-icon'>
{% block head_content %} {% block head_content %}
{% endblock head_content %} {% endblock head_content %}
{% include 'components/main/style_sheets.html' %} {% include 'components/main/style_sheets.html' %}

View File

@ -1,5 +1,7 @@
<link rel="stylesheet" <link rel="stylesheet"
href="{{ url_for('static', path='css/bulma.min.css') }}"> href="{{ url_for('static', path='css/bulma.min.css') }}">
<link rel="stylesheet"
href="{{ url_for('static', path='css/pygentic_ai.css') }}">
<link rel="stylesheet" <link rel="stylesheet"
href="{{ url_for('static', path='css/bulma.css.map') }}"> href="{{ url_for('static', path='css/bulma.css.map') }}">
<link rel="stylesheet" <link rel="stylesheet"

View File

@ -0,0 +1,4 @@
<div class="spinner-wrapper is-overlay is-hidden"
id="spinner">
<div class="loader"></div>
</div>

View File

@ -1,11 +1,12 @@
{# def {# def
div_class: str, div_class: str,
header_content: str = ''
#} #}
<article class="message is-{{ div_class }}"> <article class="message is-{{ div_class }}">
<div class="message-header"> <div class="message-header">
<p></p> <p>{{ header_content }}</p>
</div> </div>
<div class="message-body"> <div class="message-body">
{{ content }} {{ content }}

View File

@ -9,6 +9,7 @@
</div> </div>
</section> </section>
<section class="section"> <section class="section">
<Spinner></Spinner>
<div class="container"> <div class="container">
<h1 class="title">Search Here</h1> <h1 class="title">Search Here</h1>
<Search form_id="swotSearch" <Search form_id="swotSearch"
@ -17,14 +18,13 @@
method="post"> method="post">
<div class="field mt-1 pt-1"> <div class="field mt-1 pt-1">
<div class="control"> <div class="control">
<button type="button" <button type="submit"
class="button is-success" class="button is-success"
hx-indicator='#spinner'
hx-on:click=" hx-on:click="
const [status, result] = ['#status', '#result'].map(id => document.querySelector(id)); const [status, result] = ['#status', '#result'].map(id => document.querySelector(id));
status.style.display = 'block'; status.style.display = 'block';
result.style.display = 'none'; result.style.display = 'none';
htmx.trigger('#swotSearch', 'submit')
">Analyze</button> ">Analyze</button>
</div> </div>
</div> </div>
@ -36,14 +36,14 @@
id="status"> id="status">
<div class="box" <div class="box"
id="status" id="status"
hx-get='/status' hx-get={{ url_for('get_status') }}
hx-trigger='load, every 1s' hx-trigger='load, every 1s'
hx-swap='innerHTML' hx-swap='innerHTML'
style="display: none"> style="display: none">
</div> </div>
<div class="box" <div class="box"
id="result" id="result"
hx-get='/result' hx-get={{ url_for('get_result') }}
hx-trigger="load, every 1s[!this.querySelector('#result-container') || this.style.display === 'none']" hx-trigger="load, every 1s[!this.querySelector('#result-container') || this.style.display === 'none']"
hx-swap='innerHTML' hx-swap='innerHTML'
hx-on:after-request=" hx-on:after-request="
@ -57,11 +57,3 @@
</div> </div>
</section> </section>
{% endblock content %} {% endblock content %}
{% block js_content %}
<script>
document.body.addEventListener("htmx:configRequest", function(evt) {
console.log("HTMX Request Configured:", evt.detail);
});
</script>
{% endblock js_content %}

View File

@ -1,12 +1,12 @@
{% if result %} {% if result %}
<section class="section"
<section class="section"> id="result-container">
<div class="container" <div class="container"
id="result-container"> id="result-container">
<h2 class="subtitle is-2">Analysis Complete</h2> <h2 class="subtitle is-2">Analysis Complete</h2>
<div class="fixed-grid"> <div class="fixed-grid">
<div class="grid"> <div class="grid">
{% for cat in results %} {% for cat in result %}
<div class="cell"> <div class="cell">
<div class="content"> <div class="content">
<ul> <ul>

View File

@ -1,21 +1,30 @@
{% if messages %} {% if messages %}
<div id="status-container" <section class="section"
class="container"> id="status-container">
{% for message in messages %} <div class="container">
{% set is_error = message.startswith('Error:') %} {% for message in messages %}
{% set is_loading = loop.last and not result %} {% set is_error = message.startswith('Error:') %}
{% set is_tool_message = message.startswith('Using tool') %} {% set is_loading = loop.last and not result %}
<div class="box"> {% set is_tool_message = message.startswith('Using tool') %}
{% set bg_color = 'danger' if is_error else ('dark' if is_loading else "info") %} <div class="box">
{% if is_error %} {% set bg_color = 'danger' if is_error else ('dark' if is_loading else
{% set content = message.split('body:', 1)[1] %} "info") %}
{% elif is_tool_message %} {% if is_error %}
{% set content = message.split('', 2)[2].split('...', 1)[1] %} {% set header_content, content = message.split('body:', 1) %}
{% else %} {% elif is_tool_message %}
{% set content = message %} {% set header_content, content = message.split(' ', 2)[2].split('...', 1) %}
{% endif %} {% elif is_loading %}
<StatusResult div_class={{ bg_color }}>{{ message }}</StatusResult> {% set content = message %}
{% set header_content = 'In Progress' %}
{% else %}
{% set content = message %}
{% set header_content = 'Complete' %}
{% endif %}
<StatusResult div_class={{ bg_color }}
header_content={{ header_content }}>{{ message }}
</StatusResult>
</div>
{% endfor %}
</div> </div>
{% endfor %} </section>
</div>
{% endif %} {% endif %}