initial agent
This commit is contained in:
384
ai-elements-tools.md
Normal file
384
ai-elements-tools.md
Normal file
@@ -0,0 +1,384 @@
|
|||||||
|
# Generative User Interfaces
|
||||||
|
|
||||||
|
Generative user interfaces (generative UI) is the process of allowing a large language model (LLM) to go beyond text and "generate UI". This creates a more engaging and AI-native experience for users.
|
||||||
|
|
||||||
|
<WeatherSearch />
|
||||||
|
|
||||||
|
At the core of generative UI are [ tools ](/docs/ai-sdk-core/tools-and-tool-calling), which are functions you provide to the model to perform specialized tasks like getting the weather in a location. The model can decide when and how to use these tools based on the context of the conversation.
|
||||||
|
|
||||||
|
Generative UI is the process of connecting the results of a tool call to a React component. Here's how it works:
|
||||||
|
|
||||||
|
1. You provide the model with a prompt or conversation history, along with a set of tools.
|
||||||
|
2. Based on the context, the model may decide to call a tool.
|
||||||
|
3. If a tool is called, it will execute and return data.
|
||||||
|
4. This data can then be passed to a React component for rendering.
|
||||||
|
|
||||||
|
By passing the tool results to React components, you can create a generative UI experience that's more engaging and adaptive to your needs.
|
||||||
|
|
||||||
|
## Build a Generative UI Chat Interface
|
||||||
|
|
||||||
|
Let's create a chat interface that handles text-based conversations and incorporates dynamic UI elements based on model responses.
|
||||||
|
|
||||||
|
### Basic Chat Implementation
|
||||||
|
|
||||||
|
Start with a basic chat implementation using the `useChat` hook:
|
||||||
|
|
||||||
|
```tsx filename="app/page.tsx"
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useChat } from '@ai-sdk/react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const [input, setInput] = useState('');
|
||||||
|
const { messages, sendMessage } = useChat();
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
sendMessage({ text: input });
|
||||||
|
setInput('');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{messages.map(message => (
|
||||||
|
<div key={message.id}>
|
||||||
|
<div>{message.role === 'user' ? 'User: ' : 'AI: '}</div>
|
||||||
|
<div>
|
||||||
|
{message.parts.map((part, index) => {
|
||||||
|
if (part.type === 'text') {
|
||||||
|
return <span key={index}>{part.text}</span>;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<input
|
||||||
|
value={input}
|
||||||
|
onChange={e => setInput(e.target.value)}
|
||||||
|
placeholder="Type a message..."
|
||||||
|
/>
|
||||||
|
<button type="submit">Send</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To handle the chat requests and model responses, set up an API route:
|
||||||
|
|
||||||
|
```ts filename="app/api/chat/route.ts"
|
||||||
|
import { openai } from '@ai-sdk/openai';
|
||||||
|
import { streamText, convertToModelMessages, UIMessage, stepCountIs } from 'ai';
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const { messages }: { messages: UIMessage[] } = await request.json();
|
||||||
|
|
||||||
|
const result = streamText({
|
||||||
|
model: openai('gpt-4o'),
|
||||||
|
system: 'You are a friendly assistant!',
|
||||||
|
messages: convertToModelMessages(messages),
|
||||||
|
stopWhen: stepCountIs(5),
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.toUIMessageStreamResponse();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This API route uses the `streamText` function to process chat messages and stream the model's responses back to the client.
|
||||||
|
|
||||||
|
### Create a Tool
|
||||||
|
|
||||||
|
Before enhancing your chat interface with dynamic UI elements, you need to create a tool and corresponding React component. A tool will allow the model to perform a specific action, such as fetching weather information.
|
||||||
|
|
||||||
|
Create a new file called `ai/tools.ts` with the following content:
|
||||||
|
|
||||||
|
```ts filename="ai/tools.ts"
|
||||||
|
import { tool as createTool } from 'ai';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const weatherTool = createTool({
|
||||||
|
description: 'Display the weather for a location',
|
||||||
|
inputSchema: z.object({
|
||||||
|
location: z.string().describe('The location to get the weather for'),
|
||||||
|
}),
|
||||||
|
execute: async function ({ location }) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
return { weather: 'Sunny', temperature: 75, location };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const tools = {
|
||||||
|
displayWeather: weatherTool,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
In this file, you've created a tool called `weatherTool`. This tool simulates fetching weather information for a given location. This tool will return simulated data after a 2-second delay. In a real-world application, you would replace this simulation with an actual API call to a weather service.
|
||||||
|
|
||||||
|
### Update the API Route
|
||||||
|
|
||||||
|
Update the API route to include the tool you've defined:
|
||||||
|
|
||||||
|
```ts filename="app/api/chat/route.ts" highlight="3,8,14"
|
||||||
|
import { openai } from '@ai-sdk/openai';
|
||||||
|
import { streamText, convertToModelMessages, UIMessage, stepCountIs } from 'ai';
|
||||||
|
import { tools } from '@/ai/tools';
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const { messages }: { messages: UIMessage[] } = await request.json();
|
||||||
|
|
||||||
|
const result = streamText({
|
||||||
|
model: openai('gpt-4o'),
|
||||||
|
system: 'You are a friendly assistant!',
|
||||||
|
messages: convertToModelMessages(messages),
|
||||||
|
stopWhen: stepCountIs(5),
|
||||||
|
tools,
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.toUIMessageStreamResponse();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now that you've defined the tool and added it to your `streamText` call, let's build a React component to display the weather information it returns.
|
||||||
|
|
||||||
|
### Create UI Components
|
||||||
|
|
||||||
|
Create a new file called `components/weather.tsx`:
|
||||||
|
|
||||||
|
```tsx filename="components/weather.tsx"
|
||||||
|
type WeatherProps = {
|
||||||
|
temperature: number;
|
||||||
|
weather: string;
|
||||||
|
location: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Weather = ({ temperature, weather, location }: WeatherProps) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Current Weather for {location}</h2>
|
||||||
|
<p>Condition: {weather}</p>
|
||||||
|
<p>Temperature: {temperature}°C</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
This component will display the weather information for a given location. It takes three props: `temperature`, `weather`, and `location` (exactly what the `weatherTool` returns).
|
||||||
|
|
||||||
|
### Render the Weather Component
|
||||||
|
|
||||||
|
Now that you have your tool and corresponding React component, let's integrate them into your chat interface. You'll render the Weather component when the model calls the weather tool.
|
||||||
|
|
||||||
|
To check if the model has called a tool, you can check the `parts` array of the UIMessage object for tool-specific parts. In AI SDK 5.0, tool parts use typed naming: `tool-${toolName}` instead of generic types.
|
||||||
|
|
||||||
|
Update your `page.tsx` file:
|
||||||
|
|
||||||
|
```tsx filename="app/page.tsx" highlight="4,9,14-15,19-46"
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useChat } from '@ai-sdk/react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { Weather } from '@/components/weather';
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const [input, setInput] = useState('');
|
||||||
|
const { messages, sendMessage } = useChat();
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
sendMessage({ text: input });
|
||||||
|
setInput('');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{messages.map(message => (
|
||||||
|
<div key={message.id}>
|
||||||
|
<div>{message.role === 'user' ? 'User: ' : 'AI: '}</div>
|
||||||
|
<div>
|
||||||
|
{message.parts.map((part, index) => {
|
||||||
|
if (part.type === 'text') {
|
||||||
|
return <span key={index}>{part.text}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part.type === 'tool-displayWeather') {
|
||||||
|
switch (part.state) {
|
||||||
|
case 'input-available':
|
||||||
|
return <div key={index}>Loading weather...</div>;
|
||||||
|
case 'output-available':
|
||||||
|
return (
|
||||||
|
<div key={index}>
|
||||||
|
<Weather {...part.output} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case 'output-error':
|
||||||
|
return <div key={index}>Error: {part.errorText}</div>;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<input
|
||||||
|
value={input}
|
||||||
|
onChange={e => setInput(e.target.value)}
|
||||||
|
placeholder="Type a message..."
|
||||||
|
/>
|
||||||
|
<button type="submit">Send</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In this updated code snippet, you:
|
||||||
|
|
||||||
|
1. Use manual input state management with `useState` instead of the built-in `input` and `handleInputChange`.
|
||||||
|
2. Use `sendMessage` instead of `handleSubmit` to send messages.
|
||||||
|
3. Check the `parts` array of each message for different content types.
|
||||||
|
4. Handle tool parts with type `tool-displayWeather` and their different states (`input-available`, `output-available`, `output-error`).
|
||||||
|
|
||||||
|
This approach allows you to dynamically render UI components based on the model's responses, creating a more interactive and context-aware chat experience.
|
||||||
|
|
||||||
|
## Expanding Your Generative UI Application
|
||||||
|
|
||||||
|
You can enhance your chat application by adding more tools and components, creating a richer and more versatile user experience. Here's how you can expand your application:
|
||||||
|
|
||||||
|
### Adding More Tools
|
||||||
|
|
||||||
|
To add more tools, simply define them in your `ai/tools.ts` file:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Add a new stock tool
|
||||||
|
export const stockTool = createTool({
|
||||||
|
description: 'Get price for a stock',
|
||||||
|
inputSchema: z.object({
|
||||||
|
symbol: z.string().describe('The stock symbol to get the price for'),
|
||||||
|
}),
|
||||||
|
execute: async function ({ symbol }) {
|
||||||
|
// Simulated API call
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
return { symbol, price: 100 };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the tools object
|
||||||
|
export const tools = {
|
||||||
|
displayWeather: weatherTool,
|
||||||
|
getStockPrice: stockTool,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, create a new file called `components/stock.tsx`:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
type StockProps = {
|
||||||
|
price: number;
|
||||||
|
symbol: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Stock = ({ price, symbol }: StockProps) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Stock Information</h2>
|
||||||
|
<p>Symbol: {symbol}</p>
|
||||||
|
<p>Price: ${price}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, update your `page.tsx` file to include the new Stock component:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useChat } from '@ai-sdk/react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { Weather } from '@/components/weather';
|
||||||
|
import { Stock } from '@/components/stock';
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const [input, setInput] = useState('');
|
||||||
|
const { messages, sendMessage } = useChat();
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
sendMessage({ text: input });
|
||||||
|
setInput('');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{messages.map(message => (
|
||||||
|
<div key={message.id}>
|
||||||
|
<div>{message.role}</div>
|
||||||
|
<div>
|
||||||
|
{message.parts.map((part, index) => {
|
||||||
|
if (part.type === 'text') {
|
||||||
|
return <span key={index}>{part.text}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part.type === 'tool-displayWeather') {
|
||||||
|
switch (part.state) {
|
||||||
|
case 'input-available':
|
||||||
|
return <div key={index}>Loading weather...</div>;
|
||||||
|
case 'output-available':
|
||||||
|
return (
|
||||||
|
<div key={index}>
|
||||||
|
<Weather {...part.output} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case 'output-error':
|
||||||
|
return <div key={index}>Error: {part.errorText}</div>;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part.type === 'tool-getStockPrice') {
|
||||||
|
switch (part.state) {
|
||||||
|
case 'input-available':
|
||||||
|
return <div key={index}>Loading stock price...</div>;
|
||||||
|
case 'output-available':
|
||||||
|
return (
|
||||||
|
<div key={index}>
|
||||||
|
<Stock {...part.output} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case 'output-error':
|
||||||
|
return <div key={index}>Error: {part.errorText}</div>;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={input}
|
||||||
|
onChange={e => setInput(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button type="submit">Send</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
By following this pattern, you can continue to add more tools and components, expanding the capabilities of your Generative UI application.
|
||||||
@@ -19,346 +19,346 @@ agent = Agent(model=model)
|
|||||||
|
|
||||||
router = APIRouter(prefix="/api/v1/agent", tags=["Agent"])
|
router = APIRouter(prefix="/api/v1/agent", tags=["Agent"])
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"extraction": {
|
||||||
|
"core_organization_metadata": {
|
||||||
|
"ein": "84-2674654",
|
||||||
|
"legal_name": "07 IN HEAVEN MEMORIAL SCHOLARSHIP",
|
||||||
|
"phone_number": "(262) 215-0300",
|
||||||
|
"website_url": "",
|
||||||
|
"return_type": "990-PF",
|
||||||
|
"amended_return": "No",
|
||||||
|
"group_exemption_number": "",
|
||||||
|
"subsection_code": "501(c)(3)",
|
||||||
|
"ruling_date": "",
|
||||||
|
"accounting_method": "Cash",
|
||||||
|
"organization_type": "corporation",
|
||||||
|
"year_of_formation": "",
|
||||||
|
"incorporation_state": "WI",
|
||||||
|
},
|
||||||
|
"revenue_breakdown": {
|
||||||
|
"total_revenue": 5227,
|
||||||
|
"contributions_gifts_grants": 5227,
|
||||||
|
"program_service_revenue": 0,
|
||||||
|
"membership_dues": 0,
|
||||||
|
"investment_income": 0,
|
||||||
|
"gains_losses_sales_assets": 0,
|
||||||
|
"rental_income": 0,
|
||||||
|
"related_organizations_revenue": 0,
|
||||||
|
"gaming_revenue": 0,
|
||||||
|
"other_revenue": 0,
|
||||||
|
"government_grants": 0,
|
||||||
|
"foreign_contributions": 0,
|
||||||
|
},
|
||||||
|
"expenses_breakdown": {
|
||||||
|
"total_expenses": 2104,
|
||||||
|
"program_services_expenses": 0,
|
||||||
|
"management_general_expenses": 0,
|
||||||
|
"fundraising_expenses": 2104,
|
||||||
|
"grants_us_organizations": 0,
|
||||||
|
"grants_us_individuals": 0,
|
||||||
|
"grants_foreign_organizations": 0,
|
||||||
|
"grants_foreign_individuals": 0,
|
||||||
|
"compensation_officers": 0,
|
||||||
|
"compensation_other_staff": 0,
|
||||||
|
"payroll_taxes_benefits": 0,
|
||||||
|
"professional_fees": 0,
|
||||||
|
"office_occupancy_costs": 0,
|
||||||
|
"information_technology_costs": 0,
|
||||||
|
"travel_conference_expenses": 0,
|
||||||
|
"depreciation_amortization": 0,
|
||||||
|
"insurance": 0,
|
||||||
|
},
|
||||||
|
"balance_sheet": {},
|
||||||
|
"officers_directors_trustees_key_employees": [
|
||||||
|
{
|
||||||
|
"name": "REBECCA TERPSTRA",
|
||||||
|
"title_position": "PRESIDENT",
|
||||||
|
"average_hours_per_week": 0.1,
|
||||||
|
"related_party_transactions": "",
|
||||||
|
"former_officer": "",
|
||||||
|
"governance_role": "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ROBERT GUZMAN",
|
||||||
|
"title_position": "VICE PRESDEINT",
|
||||||
|
"average_hours_per_week": 0.1,
|
||||||
|
"related_party_transactions": "",
|
||||||
|
"former_officer": "",
|
||||||
|
"governance_role": "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ANDREA VALENTI",
|
||||||
|
"title_position": "TREASURER",
|
||||||
|
"average_hours_per_week": 0.1,
|
||||||
|
"related_party_transactions": "",
|
||||||
|
"former_officer": "",
|
||||||
|
"governance_role": "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BETHANY WALSH",
|
||||||
|
"title_position": "SECRETARY",
|
||||||
|
"average_hours_per_week": 0.1,
|
||||||
|
"related_party_transactions": "",
|
||||||
|
"former_officer": "",
|
||||||
|
"governance_role": "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"governance_management_disclosure": {
|
||||||
|
"governing_body_size": 4,
|
||||||
|
"independent_members": 4,
|
||||||
|
"financial_statements_reviewed": "",
|
||||||
|
"form_990_provided_to_governing_body": "",
|
||||||
|
"conflict_of_interest_policy": "",
|
||||||
|
"whistleblower_policy": "",
|
||||||
|
"document_retention_policy": "",
|
||||||
|
"ceo_compensation_review_process": "",
|
||||||
|
"public_disclosure_practices": "Yes",
|
||||||
|
},
|
||||||
|
"program_service_accomplishments": [],
|
||||||
|
"fundraising_grantmaking": {
|
||||||
|
"total_fundraising_event_revenue": 0,
|
||||||
|
"total_fundraising_event_expenses": 2104,
|
||||||
|
"professional_fundraiser_fees": 0,
|
||||||
|
},
|
||||||
|
"functional_operational_data": {
|
||||||
|
"number_of_employees": 0,
|
||||||
|
"number_of_volunteers": 0,
|
||||||
|
"occupancy_costs": 0,
|
||||||
|
"fundraising_method_descriptions": "",
|
||||||
|
"joint_ventures_disregarded_entities": "",
|
||||||
|
},
|
||||||
|
"compensation_details": {
|
||||||
|
"base_compensation": 0,
|
||||||
|
"bonus": 0,
|
||||||
|
"incentive": 0,
|
||||||
|
"other": 0,
|
||||||
|
"non_fixed_compensation": "",
|
||||||
|
"first_class_travel": "",
|
||||||
|
"housing_allowance": "",
|
||||||
|
"expense_account_usage": "",
|
||||||
|
"supplemental_retirement": "",
|
||||||
|
},
|
||||||
|
"political_lobbying_activities": {
|
||||||
|
"lobbying_expenditures_direct": 0,
|
||||||
|
"lobbying_expenditures_grassroots": 0,
|
||||||
|
"election_501h_status": "",
|
||||||
|
"political_campaign_expenditures": 0,
|
||||||
|
"related_organizations_affiliates": "",
|
||||||
|
},
|
||||||
|
"investments_endowment": {
|
||||||
|
"investment_types": "",
|
||||||
|
"donor_restricted_endowment_values": 0,
|
||||||
|
"net_appreciation_depreciation": 0,
|
||||||
|
"related_organization_transactions": "",
|
||||||
|
"loans_to_from_related_parties": "",
|
||||||
|
},
|
||||||
|
"tax_compliance_penalties": {
|
||||||
|
"penalties_excise_taxes_reported": "No",
|
||||||
|
"unrelated_business_income_disclosure": "No",
|
||||||
|
"foreign_bank_account_reporting": "No",
|
||||||
|
"schedule_o_narrative_explanations": "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"extraction_metadata": {
|
||||||
|
"core_organization_metadata": {
|
||||||
|
"ein": {"value": "84-2674654", "references": ["0-7"]},
|
||||||
|
"legal_name": {
|
||||||
|
"value": "07 IN HEAVEN MEMORIAL SCHOLARSHIP",
|
||||||
|
"references": ["0-6"],
|
||||||
|
},
|
||||||
|
"phone_number": {"value": "(262) 215-0300", "references": ["0-a"]},
|
||||||
|
"website_url": {"value": "", "references": []},
|
||||||
|
"return_type": {
|
||||||
|
"value": "990-PF",
|
||||||
|
"references": ["4ade8ed0-bce7-4bd5-bd8d-190e3e4be95b"],
|
||||||
|
},
|
||||||
|
"amended_return": {
|
||||||
|
"value": "No",
|
||||||
|
"references": ["4ac9edc4-e9bb-430f-b4c4-a42bf4c04b28"],
|
||||||
|
},
|
||||||
|
"group_exemption_number": {"value": "", "references": []},
|
||||||
|
"subsection_code": {
|
||||||
|
"value": "501(c)(3)",
|
||||||
|
"references": ["4ac9edc4-e9bb-430f-b4c4-a42bf4c04b28"],
|
||||||
|
},
|
||||||
|
"ruling_date": {"value": "", "references": []},
|
||||||
|
"accounting_method": {"value": "Cash", "references": ["0-d"]},
|
||||||
|
"organization_type": {
|
||||||
|
"value": "corporation",
|
||||||
|
"references": ["4ac9edc4-e9bb-430f-b4c4-a42bf4c04b28"],
|
||||||
|
},
|
||||||
|
"year_of_formation": {"value": "", "references": []},
|
||||||
|
"incorporation_state": {
|
||||||
|
"value": "WI",
|
||||||
|
"references": ["4ac9edc4-e9bb-430f-b4c4-a42bf4c04b28"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"revenue_breakdown": {
|
||||||
|
"total_revenue": {"value": 5227, "references": ["0-1z"]},
|
||||||
|
"contributions_gifts_grants": {"value": 5227, "references": ["0-m"]},
|
||||||
|
"program_service_revenue": {"value": 0, "references": []},
|
||||||
|
"membership_dues": {"value": 0, "references": []},
|
||||||
|
"investment_income": {"value": 0, "references": []},
|
||||||
|
"gains_losses_sales_assets": {"value": 0, "references": []},
|
||||||
|
"rental_income": {"value": 0, "references": []},
|
||||||
|
"related_organizations_revenue": {"value": 0, "references": []},
|
||||||
|
"gaming_revenue": {"value": 0, "references": []},
|
||||||
|
"other_revenue": {"value": 0, "references": []},
|
||||||
|
"government_grants": {"value": 0, "references": []},
|
||||||
|
"foreign_contributions": {"value": 0, "references": []},
|
||||||
|
},
|
||||||
|
"expenses_breakdown": {
|
||||||
|
"total_expenses": {"value": 2104, "references": ["0-2S"]},
|
||||||
|
"program_services_expenses": {"value": 0, "references": []},
|
||||||
|
"management_general_expenses": {"value": 0, "references": []},
|
||||||
|
"fundraising_expenses": {"value": 2104, "references": ["13-d"]},
|
||||||
|
"grants_us_organizations": {"value": 0, "references": []},
|
||||||
|
"grants_us_individuals": {"value": 0, "references": []},
|
||||||
|
"grants_foreign_organizations": {"value": 0, "references": []},
|
||||||
|
"grants_foreign_individuals": {"value": 0, "references": []},
|
||||||
|
"compensation_officers": {
|
||||||
|
"value": 0,
|
||||||
|
"references": ["5-1q", "5-1w", "5-1C", "5-1I"],
|
||||||
|
},
|
||||||
|
"compensation_other_staff": {"value": 0, "references": []},
|
||||||
|
"payroll_taxes_benefits": {"value": 0, "references": []},
|
||||||
|
"professional_fees": {"value": 0, "references": []},
|
||||||
|
"office_occupancy_costs": {"value": 0, "references": []},
|
||||||
|
"information_technology_costs": {"value": 0, "references": []},
|
||||||
|
"travel_conference_expenses": {"value": 0, "references": []},
|
||||||
|
"depreciation_amortization": {"value": 0, "references": []},
|
||||||
|
"insurance": {"value": 0, "references": []},
|
||||||
|
},
|
||||||
|
"balance_sheet": {},
|
||||||
|
"officers_directors_trustees_key_employees": [
|
||||||
|
{
|
||||||
|
"name": {"value": "REBECCA TERPSTRA", "references": ["5-1o"]},
|
||||||
|
"title_position": {"value": "PRESIDENT", "references": ["5-1p"]},
|
||||||
|
"average_hours_per_week": {"value": 0.1, "references": ["5-1p"]},
|
||||||
|
"related_party_transactions": {"value": "", "references": []},
|
||||||
|
"former_officer": {"value": "", "references": []},
|
||||||
|
"governance_role": {"value": "", "references": []},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": {"value": "ROBERT GUZMAN", "references": ["5-1u"]},
|
||||||
|
"title_position": {
|
||||||
|
"value": "VICE PRESDEINT",
|
||||||
|
"references": ["5-1v"],
|
||||||
|
},
|
||||||
|
"average_hours_per_week": {"value": 0.1, "references": ["5-1v"]},
|
||||||
|
"related_party_transactions": {"value": "", "references": []},
|
||||||
|
"former_officer": {"value": "", "references": []},
|
||||||
|
"governance_role": {"value": "", "references": []},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": {"value": "ANDREA VALENTI", "references": ["5-1A"]},
|
||||||
|
"title_position": {"value": "TREASURER", "references": ["5-1B"]},
|
||||||
|
"average_hours_per_week": {"value": 0.1, "references": ["5-1B"]},
|
||||||
|
"related_party_transactions": {"value": "", "references": []},
|
||||||
|
"former_officer": {"value": "", "references": []},
|
||||||
|
"governance_role": {"value": "", "references": []},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": {"value": "BETHANY WALSH", "references": ["5-1G"]},
|
||||||
|
"title_position": {"value": "SECRETARY", "references": ["5-1H"]},
|
||||||
|
"average_hours_per_week": {"value": 0.1, "references": ["5-1H"]},
|
||||||
|
"related_party_transactions": {"value": "", "references": []},
|
||||||
|
"former_officer": {"value": "", "references": []},
|
||||||
|
"governance_role": {"value": "", "references": []},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"governance_management_disclosure": {
|
||||||
|
"governing_body_size": {
|
||||||
|
"value": 4,
|
||||||
|
"references": ["5-1o", "5-1u", "5-1A", "5-1G"],
|
||||||
|
},
|
||||||
|
"independent_members": {
|
||||||
|
"value": 4,
|
||||||
|
"references": ["5-1o", "5-1u", "5-1A", "5-1G"],
|
||||||
|
},
|
||||||
|
"financial_statements_reviewed": {"value": "", "references": []},
|
||||||
|
"form_990_provided_to_governing_body": {"value": "", "references": []},
|
||||||
|
"conflict_of_interest_policy": {"value": "", "references": []},
|
||||||
|
"whistleblower_policy": {"value": "", "references": []},
|
||||||
|
"document_retention_policy": {"value": "", "references": []},
|
||||||
|
"ceo_compensation_review_process": {"value": "", "references": []},
|
||||||
|
"public_disclosure_practices": {"value": "Yes", "references": ["4-g"]},
|
||||||
|
},
|
||||||
|
"program_service_accomplishments": [],
|
||||||
|
"fundraising_grantmaking": {
|
||||||
|
"total_fundraising_event_revenue": {"value": 0, "references": []},
|
||||||
|
"total_fundraising_event_expenses": {
|
||||||
|
"value": 2104,
|
||||||
|
"references": ["13-d"],
|
||||||
|
},
|
||||||
|
"professional_fundraiser_fees": {"value": 0, "references": []},
|
||||||
|
},
|
||||||
|
"functional_operational_data": {
|
||||||
|
"number_of_employees": {"value": 0, "references": []},
|
||||||
|
"number_of_volunteers": {"value": 0, "references": []},
|
||||||
|
"occupancy_costs": {"value": 0, "references": []},
|
||||||
|
"fundraising_method_descriptions": {"value": "", "references": []},
|
||||||
|
"joint_ventures_disregarded_entities": {"value": "", "references": []},
|
||||||
|
},
|
||||||
|
"compensation_details": {
|
||||||
|
"base_compensation": {"value": 0, "references": ["5-1q", "5-1w"]},
|
||||||
|
"bonus": {"value": 0, "references": []},
|
||||||
|
"incentive": {"value": 0, "references": []},
|
||||||
|
"other": {"value": 0, "references": []},
|
||||||
|
"non_fixed_compensation": {"value": "", "references": []},
|
||||||
|
"first_class_travel": {"value": "", "references": []},
|
||||||
|
"housing_allowance": {"value": "", "references": []},
|
||||||
|
"expense_account_usage": {"value": "", "references": []},
|
||||||
|
"supplemental_retirement": {"value": "", "references": []},
|
||||||
|
},
|
||||||
|
"political_lobbying_activities": {
|
||||||
|
"lobbying_expenditures_direct": {"value": 0, "references": []},
|
||||||
|
"lobbying_expenditures_grassroots": {"value": 0, "references": []},
|
||||||
|
"election_501h_status": {"value": "", "references": []},
|
||||||
|
"political_campaign_expenditures": {"value": 0, "references": []},
|
||||||
|
"related_organizations_affiliates": {"value": "", "references": []},
|
||||||
|
},
|
||||||
|
"investments_endowment": {
|
||||||
|
"investment_types": {"value": "", "references": []},
|
||||||
|
"donor_restricted_endowment_values": {"value": 0, "references": []},
|
||||||
|
"net_appreciation_depreciation": {"value": 0, "references": []},
|
||||||
|
"related_organization_transactions": {"value": "", "references": []},
|
||||||
|
"loans_to_from_related_parties": {"value": "", "references": []},
|
||||||
|
},
|
||||||
|
"tax_compliance_penalties": {
|
||||||
|
"penalties_excise_taxes_reported": {
|
||||||
|
"value": "No",
|
||||||
|
"references": ["3-I"],
|
||||||
|
},
|
||||||
|
"unrelated_business_income_disclosure": {
|
||||||
|
"value": "No",
|
||||||
|
"references": ["3-Y"],
|
||||||
|
},
|
||||||
|
"foreign_bank_account_reporting": {
|
||||||
|
"value": "No",
|
||||||
|
"references": ["4-H"],
|
||||||
|
},
|
||||||
|
"schedule_o_narrative_explanations": {"value": "", "references": []},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"filename": "markdown.md",
|
||||||
|
"org_id": None,
|
||||||
|
"duration_ms": 16656,
|
||||||
|
"credit_usage": 27.2,
|
||||||
|
"job_id": "nnmr8lcxtykk5ll5wodjtrnn6",
|
||||||
|
"version": "extract-20250930",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
@agent.tool_plain()
|
|
||||||
|
@agent.tool_plain
|
||||||
async def build_audit_report():
|
async def build_audit_report():
|
||||||
"""Calls the audit subagent to get a full audit report of the organization"""
|
"""Calls the audit subagent to get a full audit report of the organization"""
|
||||||
data = {
|
|
||||||
"extraction": {
|
|
||||||
"core_organization_metadata": {
|
|
||||||
"ein": "84-2674654",
|
|
||||||
"legal_name": "07 IN HEAVEN MEMORIAL SCHOLARSHIP",
|
|
||||||
"phone_number": "(262) 215-0300",
|
|
||||||
"website_url": "",
|
|
||||||
"return_type": "990-PF",
|
|
||||||
"amended_return": "No",
|
|
||||||
"group_exemption_number": "",
|
|
||||||
"subsection_code": "501(c)(3)",
|
|
||||||
"ruling_date": "",
|
|
||||||
"accounting_method": "Cash",
|
|
||||||
"organization_type": "corporation",
|
|
||||||
"year_of_formation": "",
|
|
||||||
"incorporation_state": "WI",
|
|
||||||
},
|
|
||||||
"revenue_breakdown": {
|
|
||||||
"total_revenue": 5227,
|
|
||||||
"contributions_gifts_grants": 5227,
|
|
||||||
"program_service_revenue": 0,
|
|
||||||
"membership_dues": 0,
|
|
||||||
"investment_income": 0,
|
|
||||||
"gains_losses_sales_assets": 0,
|
|
||||||
"rental_income": 0,
|
|
||||||
"related_organizations_revenue": 0,
|
|
||||||
"gaming_revenue": 0,
|
|
||||||
"other_revenue": 0,
|
|
||||||
"government_grants": 0,
|
|
||||||
"foreign_contributions": 0,
|
|
||||||
},
|
|
||||||
"expenses_breakdown": {
|
|
||||||
"total_expenses": 2104,
|
|
||||||
"program_services_expenses": 0,
|
|
||||||
"management_general_expenses": 0,
|
|
||||||
"fundraising_expenses": 2104,
|
|
||||||
"grants_us_organizations": 0,
|
|
||||||
"grants_us_individuals": 0,
|
|
||||||
"grants_foreign_organizations": 0,
|
|
||||||
"grants_foreign_individuals": 0,
|
|
||||||
"compensation_officers": 0,
|
|
||||||
"compensation_other_staff": 0,
|
|
||||||
"payroll_taxes_benefits": 0,
|
|
||||||
"professional_fees": 0,
|
|
||||||
"office_occupancy_costs": 0,
|
|
||||||
"information_technology_costs": 0,
|
|
||||||
"travel_conference_expenses": 0,
|
|
||||||
"depreciation_amortization": 0,
|
|
||||||
"insurance": 0,
|
|
||||||
},
|
|
||||||
"balance_sheet": {},
|
|
||||||
"officers_directors_trustees_key_employees": [
|
|
||||||
{
|
|
||||||
"name": "REBECCA TERPSTRA",
|
|
||||||
"title_position": "PRESIDENT",
|
|
||||||
"average_hours_per_week": 0.1,
|
|
||||||
"related_party_transactions": "",
|
|
||||||
"former_officer": "",
|
|
||||||
"governance_role": "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ROBERT GUZMAN",
|
|
||||||
"title_position": "VICE PRESDEINT",
|
|
||||||
"average_hours_per_week": 0.1,
|
|
||||||
"related_party_transactions": "",
|
|
||||||
"former_officer": "",
|
|
||||||
"governance_role": "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ANDREA VALENTI",
|
|
||||||
"title_position": "TREASURER",
|
|
||||||
"average_hours_per_week": 0.1,
|
|
||||||
"related_party_transactions": "",
|
|
||||||
"former_officer": "",
|
|
||||||
"governance_role": "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "BETHANY WALSH",
|
|
||||||
"title_position": "SECRETARY",
|
|
||||||
"average_hours_per_week": 0.1,
|
|
||||||
"related_party_transactions": "",
|
|
||||||
"former_officer": "",
|
|
||||||
"governance_role": "",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"governance_management_disclosure": {
|
|
||||||
"governing_body_size": 4,
|
|
||||||
"independent_members": 4,
|
|
||||||
"financial_statements_reviewed": "",
|
|
||||||
"form_990_provided_to_governing_body": "",
|
|
||||||
"conflict_of_interest_policy": "",
|
|
||||||
"whistleblower_policy": "",
|
|
||||||
"document_retention_policy": "",
|
|
||||||
"ceo_compensation_review_process": "",
|
|
||||||
"public_disclosure_practices": "Yes",
|
|
||||||
},
|
|
||||||
"program_service_accomplishments": [],
|
|
||||||
"fundraising_grantmaking": {
|
|
||||||
"total_fundraising_event_revenue": 0,
|
|
||||||
"total_fundraising_event_expenses": 2104,
|
|
||||||
"professional_fundraiser_fees": 0,
|
|
||||||
},
|
|
||||||
"functional_operational_data": {
|
|
||||||
"number_of_employees": 0,
|
|
||||||
"number_of_volunteers": 0,
|
|
||||||
"occupancy_costs": 0,
|
|
||||||
"fundraising_method_descriptions": "",
|
|
||||||
"joint_ventures_disregarded_entities": "",
|
|
||||||
},
|
|
||||||
"compensation_details": {
|
|
||||||
"base_compensation": 0,
|
|
||||||
"bonus": 0,
|
|
||||||
"incentive": 0,
|
|
||||||
"other": 0,
|
|
||||||
"non_fixed_compensation": "",
|
|
||||||
"first_class_travel": "",
|
|
||||||
"housing_allowance": "",
|
|
||||||
"expense_account_usage": "",
|
|
||||||
"supplemental_retirement": "",
|
|
||||||
},
|
|
||||||
"political_lobbying_activities": {
|
|
||||||
"lobbying_expenditures_direct": 0,
|
|
||||||
"lobbying_expenditures_grassroots": 0,
|
|
||||||
"election_501h_status": "",
|
|
||||||
"political_campaign_expenditures": 0,
|
|
||||||
"related_organizations_affiliates": "",
|
|
||||||
},
|
|
||||||
"investments_endowment": {
|
|
||||||
"investment_types": "",
|
|
||||||
"donor_restricted_endowment_values": 0,
|
|
||||||
"net_appreciation_depreciation": 0,
|
|
||||||
"related_organization_transactions": "",
|
|
||||||
"loans_to_from_related_parties": "",
|
|
||||||
},
|
|
||||||
"tax_compliance_penalties": {
|
|
||||||
"penalties_excise_taxes_reported": "No",
|
|
||||||
"unrelated_business_income_disclosure": "No",
|
|
||||||
"foreign_bank_account_reporting": "No",
|
|
||||||
"schedule_o_narrative_explanations": "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"extraction_metadata": {
|
|
||||||
"core_organization_metadata": {
|
|
||||||
"ein": {"value": "84-2674654", "references": ["0-7"]},
|
|
||||||
"legal_name": {
|
|
||||||
"value": "07 IN HEAVEN MEMORIAL SCHOLARSHIP",
|
|
||||||
"references": ["0-6"],
|
|
||||||
},
|
|
||||||
"phone_number": {"value": "(262) 215-0300", "references": ["0-a"]},
|
|
||||||
"website_url": {"value": "", "references": []},
|
|
||||||
"return_type": {
|
|
||||||
"value": "990-PF",
|
|
||||||
"references": ["4ade8ed0-bce7-4bd5-bd8d-190e3e4be95b"],
|
|
||||||
},
|
|
||||||
"amended_return": {
|
|
||||||
"value": "No",
|
|
||||||
"references": ["4ac9edc4-e9bb-430f-b4c4-a42bf4c04b28"],
|
|
||||||
},
|
|
||||||
"group_exemption_number": {"value": "", "references": []},
|
|
||||||
"subsection_code": {
|
|
||||||
"value": "501(c)(3)",
|
|
||||||
"references": ["4ac9edc4-e9bb-430f-b4c4-a42bf4c04b28"],
|
|
||||||
},
|
|
||||||
"ruling_date": {"value": "", "references": []},
|
|
||||||
"accounting_method": {"value": "Cash", "references": ["0-d"]},
|
|
||||||
"organization_type": {
|
|
||||||
"value": "corporation",
|
|
||||||
"references": ["4ac9edc4-e9bb-430f-b4c4-a42bf4c04b28"],
|
|
||||||
},
|
|
||||||
"year_of_formation": {"value": "", "references": []},
|
|
||||||
"incorporation_state": {
|
|
||||||
"value": "WI",
|
|
||||||
"references": ["4ac9edc4-e9bb-430f-b4c4-a42bf4c04b28"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"revenue_breakdown": {
|
|
||||||
"total_revenue": {"value": 5227, "references": ["0-1z"]},
|
|
||||||
"contributions_gifts_grants": {"value": 5227, "references": ["0-m"]},
|
|
||||||
"program_service_revenue": {"value": 0, "references": []},
|
|
||||||
"membership_dues": {"value": 0, "references": []},
|
|
||||||
"investment_income": {"value": 0, "references": []},
|
|
||||||
"gains_losses_sales_assets": {"value": 0, "references": []},
|
|
||||||
"rental_income": {"value": 0, "references": []},
|
|
||||||
"related_organizations_revenue": {"value": 0, "references": []},
|
|
||||||
"gaming_revenue": {"value": 0, "references": []},
|
|
||||||
"other_revenue": {"value": 0, "references": []},
|
|
||||||
"government_grants": {"value": 0, "references": []},
|
|
||||||
"foreign_contributions": {"value": 0, "references": []},
|
|
||||||
},
|
|
||||||
"expenses_breakdown": {
|
|
||||||
"total_expenses": {"value": 2104, "references": ["0-2S"]},
|
|
||||||
"program_services_expenses": {"value": 0, "references": []},
|
|
||||||
"management_general_expenses": {"value": 0, "references": []},
|
|
||||||
"fundraising_expenses": {"value": 2104, "references": ["13-d"]},
|
|
||||||
"grants_us_organizations": {"value": 0, "references": []},
|
|
||||||
"grants_us_individuals": {"value": 0, "references": []},
|
|
||||||
"grants_foreign_organizations": {"value": 0, "references": []},
|
|
||||||
"grants_foreign_individuals": {"value": 0, "references": []},
|
|
||||||
"compensation_officers": {
|
|
||||||
"value": 0,
|
|
||||||
"references": ["5-1q", "5-1w", "5-1C", "5-1I"],
|
|
||||||
},
|
|
||||||
"compensation_other_staff": {"value": 0, "references": []},
|
|
||||||
"payroll_taxes_benefits": {"value": 0, "references": []},
|
|
||||||
"professional_fees": {"value": 0, "references": []},
|
|
||||||
"office_occupancy_costs": {"value": 0, "references": []},
|
|
||||||
"information_technology_costs": {"value": 0, "references": []},
|
|
||||||
"travel_conference_expenses": {"value": 0, "references": []},
|
|
||||||
"depreciation_amortization": {"value": 0, "references": []},
|
|
||||||
"insurance": {"value": 0, "references": []},
|
|
||||||
},
|
|
||||||
"balance_sheet": {},
|
|
||||||
"officers_directors_trustees_key_employees": [
|
|
||||||
{
|
|
||||||
"name": {"value": "REBECCA TERPSTRA", "references": ["5-1o"]},
|
|
||||||
"title_position": {"value": "PRESIDENT", "references": ["5-1p"]},
|
|
||||||
"average_hours_per_week": {"value": 0.1, "references": ["5-1p"]},
|
|
||||||
"related_party_transactions": {"value": "", "references": []},
|
|
||||||
"former_officer": {"value": "", "references": []},
|
|
||||||
"governance_role": {"value": "", "references": []},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": {"value": "ROBERT GUZMAN", "references": ["5-1u"]},
|
|
||||||
"title_position": {
|
|
||||||
"value": "VICE PRESDEINT",
|
|
||||||
"references": ["5-1v"],
|
|
||||||
},
|
|
||||||
"average_hours_per_week": {"value": 0.1, "references": ["5-1v"]},
|
|
||||||
"related_party_transactions": {"value": "", "references": []},
|
|
||||||
"former_officer": {"value": "", "references": []},
|
|
||||||
"governance_role": {"value": "", "references": []},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": {"value": "ANDREA VALENTI", "references": ["5-1A"]},
|
|
||||||
"title_position": {"value": "TREASURER", "references": ["5-1B"]},
|
|
||||||
"average_hours_per_week": {"value": 0.1, "references": ["5-1B"]},
|
|
||||||
"related_party_transactions": {"value": "", "references": []},
|
|
||||||
"former_officer": {"value": "", "references": []},
|
|
||||||
"governance_role": {"value": "", "references": []},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": {"value": "BETHANY WALSH", "references": ["5-1G"]},
|
|
||||||
"title_position": {"value": "SECRETARY", "references": ["5-1H"]},
|
|
||||||
"average_hours_per_week": {"value": 0.1, "references": ["5-1H"]},
|
|
||||||
"related_party_transactions": {"value": "", "references": []},
|
|
||||||
"former_officer": {"value": "", "references": []},
|
|
||||||
"governance_role": {"value": "", "references": []},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"governance_management_disclosure": {
|
|
||||||
"governing_body_size": {
|
|
||||||
"value": 4,
|
|
||||||
"references": ["5-1o", "5-1u", "5-1A", "5-1G"],
|
|
||||||
},
|
|
||||||
"independent_members": {
|
|
||||||
"value": 4,
|
|
||||||
"references": ["5-1o", "5-1u", "5-1A", "5-1G"],
|
|
||||||
},
|
|
||||||
"financial_statements_reviewed": {"value": "", "references": []},
|
|
||||||
"form_990_provided_to_governing_body": {"value": "", "references": []},
|
|
||||||
"conflict_of_interest_policy": {"value": "", "references": []},
|
|
||||||
"whistleblower_policy": {"value": "", "references": []},
|
|
||||||
"document_retention_policy": {"value": "", "references": []},
|
|
||||||
"ceo_compensation_review_process": {"value": "", "references": []},
|
|
||||||
"public_disclosure_practices": {"value": "Yes", "references": ["4-g"]},
|
|
||||||
},
|
|
||||||
"program_service_accomplishments": [],
|
|
||||||
"fundraising_grantmaking": {
|
|
||||||
"total_fundraising_event_revenue": {"value": 0, "references": []},
|
|
||||||
"total_fundraising_event_expenses": {
|
|
||||||
"value": 2104,
|
|
||||||
"references": ["13-d"],
|
|
||||||
},
|
|
||||||
"professional_fundraiser_fees": {"value": 0, "references": []},
|
|
||||||
},
|
|
||||||
"functional_operational_data": {
|
|
||||||
"number_of_employees": {"value": 0, "references": []},
|
|
||||||
"number_of_volunteers": {"value": 0, "references": []},
|
|
||||||
"occupancy_costs": {"value": 0, "references": []},
|
|
||||||
"fundraising_method_descriptions": {"value": "", "references": []},
|
|
||||||
"joint_ventures_disregarded_entities": {"value": "", "references": []},
|
|
||||||
},
|
|
||||||
"compensation_details": {
|
|
||||||
"base_compensation": {"value": 0, "references": ["5-1q", "5-1w"]},
|
|
||||||
"bonus": {"value": 0, "references": []},
|
|
||||||
"incentive": {"value": 0, "references": []},
|
|
||||||
"other": {"value": 0, "references": []},
|
|
||||||
"non_fixed_compensation": {"value": "", "references": []},
|
|
||||||
"first_class_travel": {"value": "", "references": []},
|
|
||||||
"housing_allowance": {"value": "", "references": []},
|
|
||||||
"expense_account_usage": {"value": "", "references": []},
|
|
||||||
"supplemental_retirement": {"value": "", "references": []},
|
|
||||||
},
|
|
||||||
"political_lobbying_activities": {
|
|
||||||
"lobbying_expenditures_direct": {"value": 0, "references": []},
|
|
||||||
"lobbying_expenditures_grassroots": {"value": 0, "references": []},
|
|
||||||
"election_501h_status": {"value": "", "references": []},
|
|
||||||
"political_campaign_expenditures": {"value": 0, "references": []},
|
|
||||||
"related_organizations_affiliates": {"value": "", "references": []},
|
|
||||||
},
|
|
||||||
"investments_endowment": {
|
|
||||||
"investment_types": {"value": "", "references": []},
|
|
||||||
"donor_restricted_endowment_values": {"value": 0, "references": []},
|
|
||||||
"net_appreciation_depreciation": {"value": 0, "references": []},
|
|
||||||
"related_organization_transactions": {"value": "", "references": []},
|
|
||||||
"loans_to_from_related_parties": {"value": "", "references": []},
|
|
||||||
},
|
|
||||||
"tax_compliance_penalties": {
|
|
||||||
"penalties_excise_taxes_reported": {
|
|
||||||
"value": "No",
|
|
||||||
"references": ["3-I"],
|
|
||||||
},
|
|
||||||
"unrelated_business_income_disclosure": {
|
|
||||||
"value": "No",
|
|
||||||
"references": ["3-Y"],
|
|
||||||
},
|
|
||||||
"foreign_bank_account_reporting": {
|
|
||||||
"value": "No",
|
|
||||||
"references": ["4-H"],
|
|
||||||
},
|
|
||||||
"schedule_o_narrative_explanations": {"value": "", "references": []},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"metadata": {
|
|
||||||
"filename": "markdown.md",
|
|
||||||
"org_id": None,
|
|
||||||
"duration_ms": 16656,
|
|
||||||
"credit_usage": 27.2,
|
|
||||||
"job_id": "nnmr8lcxtykk5ll5wodjtrnn6",
|
|
||||||
"version": "extract-20250930",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
result = await form_auditor.build_audit_report(data)
|
result = await form_auditor.build_audit_report(data)
|
||||||
|
|
||||||
return result.model_dump_json()
|
return result.model_dump()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/chat")
|
@router.post("/chat")
|
||||||
|
|||||||
@@ -27,11 +27,12 @@ services:
|
|||||||
|
|
||||||
db:
|
db:
|
||||||
image: redis/redis-stack:latest
|
image: redis/redis-stack:latest
|
||||||
|
command: redis-server --appendonly yes
|
||||||
ports:
|
ports:
|
||||||
- 6379:6379
|
- 6379:6379
|
||||||
- 8001:8001
|
- 8001:8001
|
||||||
volumes:
|
volumes:
|
||||||
- redis_data:/data # Persistent Redis data
|
- ./redis_data:/data
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- app-network
|
- app-network
|
||||||
|
|||||||
283
frontend/src/components/AuditReport.tsx
Normal file
283
frontend/src/components/AuditReport.tsx
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
AlertTriangle,
|
||||||
|
CheckCircle,
|
||||||
|
XCircle,
|
||||||
|
FileText,
|
||||||
|
Building,
|
||||||
|
Calendar,
|
||||||
|
AlertCircle,
|
||||||
|
TrendingUp,
|
||||||
|
Shield,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
type Severity = "Pass" | "Warning" | "Error";
|
||||||
|
|
||||||
|
interface AuditFinding {
|
||||||
|
check_id: string;
|
||||||
|
category: string;
|
||||||
|
severity: Severity;
|
||||||
|
message: string;
|
||||||
|
mitigation?: string;
|
||||||
|
confidence: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AuditSectionSummary {
|
||||||
|
section: string;
|
||||||
|
severity: Severity;
|
||||||
|
summary: string;
|
||||||
|
confidence: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AuditReportData {
|
||||||
|
organisation_ein: string;
|
||||||
|
organisation_name: string;
|
||||||
|
year?: number;
|
||||||
|
overall_severity: Severity;
|
||||||
|
findings: AuditFinding[];
|
||||||
|
sections: AuditSectionSummary[];
|
||||||
|
overall_summary?: string;
|
||||||
|
notes?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AuditReportProps {
|
||||||
|
data: AuditReportData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSeverityIcon = (severity: Severity) => {
|
||||||
|
switch (severity) {
|
||||||
|
case "Pass":
|
||||||
|
return <CheckCircle className="w-5 h-5 text-green-600" />;
|
||||||
|
case "Warning":
|
||||||
|
return <AlertTriangle className="w-5 h-5 text-yellow-600" />;
|
||||||
|
case "Error":
|
||||||
|
return <XCircle className="w-5 h-5 text-red-600" />;
|
||||||
|
default:
|
||||||
|
return <AlertCircle className="w-5 h-5 text-gray-600" />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSeverityColor = (severity: Severity) => {
|
||||||
|
switch (severity) {
|
||||||
|
case "Pass":
|
||||||
|
return "text-green-700 bg-green-50 border-green-200";
|
||||||
|
case "Warning":
|
||||||
|
return "text-yellow-700 bg-yellow-50 border-yellow-200";
|
||||||
|
case "Error":
|
||||||
|
return "text-red-700 bg-red-50 border-red-200";
|
||||||
|
default:
|
||||||
|
return "text-gray-700 bg-gray-50 border-gray-200";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getConfidenceColor = (confidence: number) => {
|
||||||
|
if (confidence >= 0.8) return "text-green-600";
|
||||||
|
if (confidence >= 0.6) return "text-yellow-600";
|
||||||
|
return "text-red-600";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AuditReport: React.FC<AuditReportProps> = ({ data }) => {
|
||||||
|
const {
|
||||||
|
organisation_ein,
|
||||||
|
organisation_name,
|
||||||
|
year,
|
||||||
|
overall_severity,
|
||||||
|
findings,
|
||||||
|
sections,
|
||||||
|
overall_summary,
|
||||||
|
notes,
|
||||||
|
} = data;
|
||||||
|
|
||||||
|
const severityStats = {
|
||||||
|
Pass: findings.filter((f) => f.severity === "Pass").length,
|
||||||
|
Warning: findings.filter((f) => f.severity === "Warning").length,
|
||||||
|
Error: findings.filter((f) => f.severity === "Error").length,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden">
|
||||||
|
<div>
|
||||||
|
{/* Header */}
|
||||||
|
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-200 p-3">
|
||||||
|
<div className="flex items-start justify-between">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="p-2 bg-blue-100 rounded-lg">
|
||||||
|
<Shield className="w-6 h-6 text-blue-600" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-semibold text-gray-900 flex items-center gap-2">
|
||||||
|
<Building className="w-5 h-5" />
|
||||||
|
{organisation_name}
|
||||||
|
</h2>
|
||||||
|
<div className="flex items-center gap-4 mt-1 text-sm text-gray-600">
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<FileText className="w-4 h-4" />
|
||||||
|
EIN: {organisation_ein}
|
||||||
|
</span>
|
||||||
|
{year && (
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Calendar className="w-4 h-4" />
|
||||||
|
{year}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Overall Status */}
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex items-center gap-2 px-4 py-2 rounded-full border",
|
||||||
|
getSeverityColor(overall_severity),
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{getSeverityIcon(overall_severity)}
|
||||||
|
<span className="font-medium">{overall_severity}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Statistics Bar */}
|
||||||
|
<div className="bg-gray-50 px-4 py-2 border-b border-gray-200">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h3 className="text-sm font-medium text-gray-700">Audit Summary</h3>
|
||||||
|
<div className="flex items-center gap-6 text-sm">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<CheckCircle className="w-4 h-4 text-green-600" />
|
||||||
|
<span className="text-green-700 font-medium">
|
||||||
|
{severityStats.Pass}
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-600">Passed</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<AlertTriangle className="w-4 h-4 text-yellow-600" />
|
||||||
|
<span className="text-yellow-700 font-medium">
|
||||||
|
{severityStats.Warning}
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-600">Warnings</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<XCircle className="w-4 h-4 text-red-600" />
|
||||||
|
<span className="text-red-700 font-medium">
|
||||||
|
{severityStats.Error}
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-600">Errors</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Overall Summary */}
|
||||||
|
{overall_summary && (
|
||||||
|
<div className="p-4 border-b border-gray-200">
|
||||||
|
<h3 className="text-lg font-medium text-gray-900 mb-3">
|
||||||
|
Overall Assessment
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-700 leading-relaxed">{overall_summary}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Section Summaries */}
|
||||||
|
{sections.length > 0 && (
|
||||||
|
<div className="p-4 border-b border-gray-200">
|
||||||
|
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
||||||
|
Section Analysis
|
||||||
|
</h3>
|
||||||
|
<div className="grid gap-2 sm:grid-cols-2">
|
||||||
|
{sections.map((section, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={cn(
|
||||||
|
"border rounded-lg p-3",
|
||||||
|
getSeverityColor(section.severity),
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<h4 className="font-medium">{section.section}</h4>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{getSeverityIcon(section.severity)}
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"text-xs font-medium",
|
||||||
|
getConfidenceColor(section.confidence),
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{Math.round(section.confidence * 100)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm opacity-90">{section.summary}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Detailed Findings */}
|
||||||
|
<div className="p-4">
|
||||||
|
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
||||||
|
Detailed Findings ({findings.length})
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-3">
|
||||||
|
{findings.map((finding, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={cn(
|
||||||
|
"border rounded-lg p-3",
|
||||||
|
getSeverityColor(finding.severity),
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between mb-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{getSeverityIcon(finding.severity)}
|
||||||
|
<div>
|
||||||
|
<span className="font-medium">{finding.category}</span>
|
||||||
|
<span className="text-sm text-gray-500 ml-2">
|
||||||
|
#{finding.check_id}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<TrendingUp className="w-4 h-4 text-gray-400" />
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"text-sm font-medium",
|
||||||
|
getConfidenceColor(finding.confidence),
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{Math.round(finding.confidence * 100)}% confidence
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-sm mb-2 leading-relaxed">
|
||||||
|
{finding.message}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{finding.mitigation && (
|
||||||
|
<div className="bg-white bg-opacity-50 rounded p-2 border border-current border-opacity-20">
|
||||||
|
<h5 className="font-medium text-sm mb-1">
|
||||||
|
Recommended Action:
|
||||||
|
</h5>
|
||||||
|
<p className="text-sm">{finding.mitigation}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Notes */}
|
||||||
|
{notes && (
|
||||||
|
<div className="bg-gray-50 p-3 border-t border-gray-200">
|
||||||
|
<h3 className="text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Additional Notes
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-600 italic">{notes}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
AlertCircle,
|
AlertCircle,
|
||||||
PaperclipIcon,
|
PaperclipIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import { AuditReport } from "./AuditReport";
|
||||||
import { Loader } from "@/components/ai-elements/loader";
|
import { Loader } from "@/components/ai-elements/loader";
|
||||||
import { DefaultChatTransport } from "ai";
|
import { DefaultChatTransport } from "ai";
|
||||||
|
|
||||||
@@ -101,9 +102,9 @@ export function ChatTab({ selectedTema }: ChatTabProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-[638px] max-h-[638px]">
|
||||||
{/* Chat Header */}
|
{/* Chat Header */}
|
||||||
<div className="border-b border-gray-200 px-6 py-4">
|
<div className="border-b border-gray-200 px-6 py-4 flex-shrink-0">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="p-2 bg-blue-100 rounded-lg">
|
<div className="p-2 bg-blue-100 rounded-lg">
|
||||||
<MessageCircle className="w-5 h-5 text-blue-600" />
|
<MessageCircle className="w-5 h-5 text-blue-600" />
|
||||||
@@ -120,138 +121,178 @@ export function ChatTab({ selectedTema }: ChatTabProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Chat Content */}
|
{/* Chat Content */}
|
||||||
<div className="flex-1 flex flex-col">
|
<div className="flex-1 min-h-0 overflow-y-auto">
|
||||||
<Conversation className="flex-1">
|
<div className="max-w-4xl mx-auto w-full space-y-6 p-6">
|
||||||
<ConversationContent className="p-6">
|
{/* Welcome Message */}
|
||||||
<div className="max-w-4xl mx-auto w-full">
|
{messages.length === 0 && (
|
||||||
{/* Welcome Message */}
|
<div className="flex items-start gap-3 mb-6">
|
||||||
{messages.length === 0 && (
|
<div className="p-2 bg-blue-100 rounded-full">
|
||||||
<div className="flex items-start gap-3 mb-6">
|
<Bot className="w-4 h-4 text-blue-600" />
|
||||||
<div className="p-2 bg-blue-100 rounded-full">
|
</div>
|
||||||
<Bot className="w-4 h-4 text-blue-600" />
|
<div className="flex-1 bg-gray-50 rounded-lg p-4">
|
||||||
</div>
|
<p className="text-sm text-gray-800">
|
||||||
<div className="flex-1 bg-gray-50 rounded-lg p-4">
|
¡Hola! Soy tu asistente de IA para el dataroom{" "}
|
||||||
<p className="text-sm text-gray-800">
|
<strong>{selectedTema}</strong>. Puedes hacerme preguntas
|
||||||
¡Hola! Soy tu asistente de IA para el dataroom{" "}
|
sobre los documentos almacenados aquí.
|
||||||
<strong>{selectedTema}</strong>. Puedes hacerme preguntas
|
</p>
|
||||||
sobre los documentos almacenados aquí.
|
</div>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Error Message */}
|
{/* Error Message */}
|
||||||
{error && (
|
{error && (
|
||||||
<div className="flex items-start gap-3 mb-4">
|
<div className="flex items-start gap-3 mb-4">
|
||||||
<div className="p-2 bg-red-100 rounded-full">
|
<div className="p-2 bg-red-100 rounded-full">
|
||||||
<AlertCircle className="w-4 h-4 text-red-600" />
|
<AlertCircle className="w-4 h-4 text-red-600" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 bg-red-50 rounded-lg p-4 border border-red-200">
|
<div className="flex-1 bg-red-50 rounded-lg p-4 border border-red-200">
|
||||||
<p className="text-sm text-red-800">{error}</p>
|
<p className="text-sm text-red-800">{error}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Chat Messages */}
|
{/* Chat Messages */}
|
||||||
{messages.map((message) => (
|
{messages.map((message) => (
|
||||||
<div key={message.id}>
|
<div key={message.id}>
|
||||||
{message.parts.map((part, i) => {
|
{message.parts.map((part, i) => {
|
||||||
switch (part.type) {
|
switch (part.type) {
|
||||||
case "text":
|
case "text":
|
||||||
|
return (
|
||||||
|
<Fragment key={`${message.id}-${i}`}>
|
||||||
|
<Message from={message.role} className="max-w-none">
|
||||||
|
<MessageContent>
|
||||||
|
<Response>{part.text}</Response>
|
||||||
|
</MessageContent>
|
||||||
|
</Message>
|
||||||
|
{message.role === "assistant" &&
|
||||||
|
i === message.parts.length - 1 && (
|
||||||
|
<Actions className="mt-2">
|
||||||
|
<Action
|
||||||
|
onClick={() => regenerate()}
|
||||||
|
label="Regenerar"
|
||||||
|
disabled={status === "streaming"}
|
||||||
|
>
|
||||||
|
<RefreshCcwIcon className="size-3" />
|
||||||
|
</Action>
|
||||||
|
<Action
|
||||||
|
onClick={() =>
|
||||||
|
navigator.clipboard.writeText(part.text)
|
||||||
|
}
|
||||||
|
label="Copiar"
|
||||||
|
>
|
||||||
|
<CopyIcon className="size-3" />
|
||||||
|
</Action>
|
||||||
|
</Actions>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
case "tool-build_audit_report":
|
||||||
|
switch (part.state) {
|
||||||
|
case "input-available":
|
||||||
return (
|
return (
|
||||||
<Fragment key={`${message.id}-${i}`}>
|
<div
|
||||||
<Message from={message.role} className="max-w-none">
|
key={`${message.id}-${i}`}
|
||||||
<MessageContent>
|
className="flex items-center gap-2 p-4 bg-blue-50 rounded-lg border border-blue-200"
|
||||||
<Response>{part.text}</Response>
|
>
|
||||||
</MessageContent>
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600"></div>
|
||||||
</Message>
|
<span className="text-sm text-blue-700">
|
||||||
{message.role === "assistant" &&
|
Generando reporte de auditoría...
|
||||||
i === message.parts.length - 1 && (
|
</span>
|
||||||
<Actions className="mt-2">
|
</div>
|
||||||
<Action
|
);
|
||||||
onClick={() => regenerate()}
|
case "output-available":
|
||||||
label="Regenerar"
|
return (
|
||||||
disabled={status === "streaming"}
|
<div
|
||||||
>
|
key={`${message.id}-${i}`}
|
||||||
<RefreshCcwIcon className="size-3" />
|
className="mt-4 w-full"
|
||||||
</Action>
|
>
|
||||||
<Action
|
<div className="max-w-full overflow-hidden">
|
||||||
onClick={() =>
|
<AuditReport data={part.output} />
|
||||||
navigator.clipboard.writeText(part.text)
|
</div>
|
||||||
}
|
</div>
|
||||||
label="Copiar"
|
);
|
||||||
>
|
case "output-error":
|
||||||
<CopyIcon className="size-3" />
|
return (
|
||||||
</Action>
|
<div
|
||||||
</Actions>
|
key={`${message.id}-${i}`}
|
||||||
)}
|
className="p-4 bg-red-50 border border-red-200 rounded-lg"
|
||||||
</Fragment>
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<AlertCircle className="w-4 h-4 text-red-600" />
|
||||||
|
<span className="text-sm font-medium text-red-800">
|
||||||
|
Error generando reporte de auditoría
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-red-600 mt-1">
|
||||||
|
{part.errorText}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
})}
|
default:
|
||||||
</div>
|
return null;
|
||||||
))}
|
|
||||||
{status === "streaming" && <Loader />}
|
|
||||||
{status === "loading" && <Loader />}
|
|
||||||
</div>
|
|
||||||
</ConversationContent>
|
|
||||||
<ConversationScrollButton />
|
|
||||||
</Conversation>
|
|
||||||
|
|
||||||
{/* Chat Input */}
|
|
||||||
<div className="border-t border-gray-200 p-6 bg-gray-50/50">
|
|
||||||
<PromptInput
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
className="max-w-4xl mx-auto border-2 border-gray-200 rounded-xl focus-within:border-slate-500 transition-colors duration-200 bg-white"
|
|
||||||
globalDrop
|
|
||||||
multiple
|
|
||||||
>
|
|
||||||
<PromptInputHeader className="p-2 pb-0">
|
|
||||||
<PromptInputAttachments>
|
|
||||||
{(attachment) => <PromptInputAttachment data={attachment} />}
|
|
||||||
</PromptInputAttachments>
|
|
||||||
</PromptInputHeader>
|
|
||||||
<PromptInputBody>
|
|
||||||
<PromptInputTextarea
|
|
||||||
onChange={(e) => setInput(e.target.value)}
|
|
||||||
value={input}
|
|
||||||
placeholder={`Pregunta algo sobre ${selectedTema}...`}
|
|
||||||
disabled={status === "streaming" || status === "loading"}
|
|
||||||
className="min-h-[60px] resize-none border-0 focus:ring-0 transition-all duration-200 text-base px-4 py-3 bg-white rounded-xl"
|
|
||||||
/>
|
|
||||||
</PromptInputBody>
|
|
||||||
<PromptInputFooter className="mt-3 flex justify-between items-center">
|
|
||||||
<PromptInputTools>
|
|
||||||
<PromptInputActionMenu>
|
|
||||||
<PromptInputActionMenuTrigger>
|
|
||||||
<PaperclipIcon className="size-4" />
|
|
||||||
</PromptInputActionMenuTrigger>
|
|
||||||
<PromptInputActionMenuContent>
|
|
||||||
<PromptInputActionAddAttachments />
|
|
||||||
</PromptInputActionMenuContent>
|
|
||||||
</PromptInputActionMenu>
|
|
||||||
</PromptInputTools>
|
|
||||||
<PromptInputSubmit
|
|
||||||
disabled={
|
|
||||||
(!input.trim() && !status) ||
|
|
||||||
status === "streaming" ||
|
|
||||||
status === "loading"
|
|
||||||
}
|
}
|
||||||
status={status}
|
})}
|
||||||
className={`rounded-full px-6 py-2 font-medium transition-all duration-200 flex items-center gap-2 ${
|
</div>
|
||||||
(!input.trim() && !status) ||
|
))}
|
||||||
status === "streaming" ||
|
{status === "streaming" && <Loader />}
|
||||||
status === "loading"
|
{status === "loading" && <Loader />}
|
||||||
? "bg-gray-300 cursor-not-allowed text-gray-500"
|
|
||||||
: "bg-blue-600 hover:bg-blue-700 text-white"
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</PromptInputFooter>
|
|
||||||
</PromptInput>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Chat Input */}
|
||||||
|
<div className="border-t border-gray-200 p-3 bg-gray-50/50 flex-shrink-0">
|
||||||
|
<PromptInput
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className="max-w-4xl mx-auto border-2 border-gray-200 rounded-xl focus-within:border-slate-500 transition-colors duration-200 bg-white"
|
||||||
|
globalDrop
|
||||||
|
multiple
|
||||||
|
>
|
||||||
|
<PromptInputHeader className="p-2 pb-0">
|
||||||
|
<PromptInputAttachments>
|
||||||
|
{(attachment) => <PromptInputAttachment data={attachment} />}
|
||||||
|
</PromptInputAttachments>
|
||||||
|
</PromptInputHeader>
|
||||||
|
<PromptInputBody>
|
||||||
|
<PromptInputTextarea
|
||||||
|
onChange={(e) => setInput(e.target.value)}
|
||||||
|
value={input}
|
||||||
|
placeholder={`Pregunta algo sobre ${selectedTema}...`}
|
||||||
|
disabled={status === "streaming" || status === "loading"}
|
||||||
|
className="min-h-[60px] resize-none border-0 focus:ring-0 transition-all duration-200 text-base px-4 py-3 bg-white rounded-xl"
|
||||||
|
/>
|
||||||
|
</PromptInputBody>
|
||||||
|
<PromptInputFooter className="mt-3 flex justify-between items-center">
|
||||||
|
<PromptInputTools>
|
||||||
|
<PromptInputActionMenu>
|
||||||
|
<PromptInputActionMenuTrigger>
|
||||||
|
<PaperclipIcon className="size-4" />
|
||||||
|
</PromptInputActionMenuTrigger>
|
||||||
|
<PromptInputActionMenuContent>
|
||||||
|
<PromptInputActionAddAttachments />
|
||||||
|
</PromptInputActionMenuContent>
|
||||||
|
</PromptInputActionMenu>
|
||||||
|
</PromptInputTools>
|
||||||
|
<PromptInputSubmit
|
||||||
|
disabled={
|
||||||
|
(!input.trim() && !status) ||
|
||||||
|
status === "streaming" ||
|
||||||
|
status === "loading"
|
||||||
|
}
|
||||||
|
status={status}
|
||||||
|
className={`rounded-full px-6 py-2 font-medium transition-all duration-200 flex items-center gap-2 ${
|
||||||
|
(!input.trim() && !status) ||
|
||||||
|
status === "streaming" ||
|
||||||
|
status === "loading"
|
||||||
|
? "bg-gray-300 cursor-not-allowed text-gray-500"
|
||||||
|
: "bg-blue-600 hover:bg-blue-700 text-white"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</PromptInputFooter>
|
||||||
|
</PromptInput>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
redis_data/dump.rdb
Normal file
BIN
redis_data/dump.rdb
Normal file
Binary file not shown.
Reference in New Issue
Block a user