Skip to content

Commit

Permalink
making progress on the reporting tool
Browse files Browse the repository at this point in the history
  • Loading branch information
alnutile committed Jul 9, 2024
1 parent 3f970c8 commit 12d6611
Show file tree
Hide file tree
Showing 20 changed files with 418 additions and 103 deletions.
5 changes: 5 additions & 0 deletions Modules/LlmDriver/app/Functions/FunctionContract.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ protected function getName(): string
return $this->name;
}

protected function getKey(): string
{
return $this->name;
}

protected function getDescription(): string
{
return $this->description;
Expand Down
88 changes: 65 additions & 23 deletions Modules/LlmDriver/app/Functions/ReportingTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

namespace LlmLaraHub\LlmDriver\Functions;

use App\Domains\Prompts\ReportBuildingFindRequirementsPrompt;
use App\Domains\Prompts\StandardsCheckerPrompt;
use App\Domains\Reporting\ReportTypeEnum;
use App\Models\Message;
use App\Models\Report;
use App\Models\Section;
use Illuminate\Support\Facades\Log;
use LlmLaraHub\LlmDriver\LlmDriverFacade;
use LlmLaraHub\LlmDriver\Requests\MessageInDto;
Expand All @@ -26,47 +30,85 @@ public function handle(
{
Log::info('[LaraChain] ReportingTool Function called');

$usersInput = MessageInDto::fromMessageAsUser($message);
//make or update a reports table for this message - chat
//gather all the documents
//and for each document
//build up a list of sections that are requests (since this is a flexible tool that will be part of a prompt
//then save each one with a reference to the document, chunk to the sections table
//then for each section review each related collections solutions to make numerous
// or use vector search
//entries to address the sections requirements
//saving to the entries the related collection, document, document_chunk, full text (siblings)
//then build a response for each section to the response field of the section.
//finally build up a summary of all the responses for the report
//this will lead to a ui to comment on "sections" and "responses"

$report = Report::firstOrCreate([
'chat_id' => $message->getChat()->id,
'reference_collection_id' => $message->getReferenceCollection()?->id,
'user_id' => $message->getChat()->user_id,
], [
'type' => ReportTypeEnum::RFP,
]);


$documents = $message->getChatable()->documents;

notify_ui($message->getChat(), 'Going through all the documents to check standards');
notify_ui($message->getChat(), 'Going through all the documents to check requirements');

$this->results = [];

foreach ($documents->chunk(3) as $index => $chunk) {
foreach($documents->chunk(3) as $index => $databaseChunk) {
try {

$prompts = [];
$documents = [];

foreach ($chunk as $document) {
if ($document->summary) {
/**
* @NOTE
* This assumes a small amount of incoming content to check
* The user my upload a blog post that is 20 paragraphs or more.
*/
$prompt = StandardsCheckerPrompt::prompt(
$document->summary, $usersInput->content
);
$this->promptHistory[] = $prompt;
$prompts[] = $prompt;
} else {
Log::info('[LaraChain] No Summary for Document', [
'document' => $document->id,
]);
}
foreach ($databaseChunk as $document) {
$documents[] = $document;
$content = $document->document_chunks->pluck('content')->toArray();

$content = implode("\n", $content);

/**
* @NOTE
* This assumes a small amount of incoming content to check
* The user my upload a blog post that is 20 paragraphs or more.
*/
$prompt = ReportBuildingFindRequirementsPrompt::prompt(
$content, $message->getContent(), $message->getChatable()->description
);
$this->promptHistory[] = $prompt;
$prompts[] = $prompt;

}

$results = LlmDriverFacade::driver($message->getDriver())
->completionPool($prompts);

foreach ($results as $result) {
$this->results[] = $result->content;
//make the sections per the results coming back.
$content = $result->content;
$content = json_decode($content, true);
foreach($content as $sectionIndex =>$sectionText) {
$title = data_get($sectionText, 'title', "NOT TITLE GIVEN");
$content = data_get($sectionText, 'content', "NOT CONTENT GIVEN");

$section = Section::updateOrCreate([
'document_id' => $document->id,
'report_id' => $report->id,
'sort_order' => $sectionIndex,
],[
'subject' => $title,
'content' => $content,
]);
}

$this->results[] = $section->content;

}

} catch (\Exception $e) {
Log::error('Error running Standards Checker', [
Log::error('Error running Reporting Tool Checker', [
'error' => $e->getMessage(),
'index' => $index,
]);
Expand Down
12 changes: 12 additions & 0 deletions Modules/LlmDriver/app/LlmDriverClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace LlmLaraHub\LlmDriver;

use LlmLaraHub\LlmDriver\Functions\ReportingTool;
use LlmLaraHub\LlmDriver\Functions\SearchAndSummarize;
use LlmLaraHub\LlmDriver\Functions\StandardsChecker;
use LlmLaraHub\LlmDriver\Functions\SummarizeCollection;
Expand Down Expand Up @@ -59,9 +60,20 @@ public function getFunctions(): array
(new SummarizeCollection())->getFunction(),
(new SearchAndSummarize())->getFunction(),
(new StandardsChecker())->getFunction(),
(new ReportingTool())->getFunction(),
];
}

public function getFunctionsForUi(): array
{
return collect($this->getFunctions())
->map(function($item) {
$item = $item->toArray();
$item['name_formatted'] = str($item['name'])->headline()->toString();
return $item;
})->toArray();
}

/**
* @NOTE
* Some systems like Ollama might not like all the traffic
Expand Down
6 changes: 6 additions & 0 deletions Modules/LlmDriver/app/LlmServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;
use LlmLaraHub\LlmDriver\DistanceQuery\DistanceQueryClient;
use LlmLaraHub\LlmDriver\Functions\ReportingTool;
use LlmLaraHub\LlmDriver\Functions\SearchAndSummarize;
use LlmLaraHub\LlmDriver\Functions\StandardsChecker;
use LlmLaraHub\LlmDriver\Functions\SummarizeCollection;
Expand Down Expand Up @@ -68,6 +69,11 @@ public function boot(): void
return new StandardsChecker();
});

$this->app->bind('reporting_tool', function () {
return new ReportingTool();
});


}

/**
Expand Down
39 changes: 13 additions & 26 deletions Modules/LlmDriver/app/Orchestrate.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ public function handle(
*/
$messagesArray = $message->getLatestMessages();

put_fixture('latest_messages.json', $messagesArray);

$filter = $message->meta_data?->filter;

Expand All @@ -59,33 +58,21 @@ public function handle(
'tool' => $tool,
]);

/**
* @TODO
* sooo much to do
* this has to be just a natural function
* but the user is now forcing it which is fine too
*/
if ($tool === 'standards_checker') {
/**
* @TODO
* Refactor this since Message really can build this
* and I am now passing this into all things.
* Will come back shortly
*/
$functionDto = FunctionCallDto::from([
'arguments' => '{}',
'function_name' => 'standards_checker',
'filter' => $filter,
]);
$functionDto = FunctionCallDto::from([
'arguments' => '{}',
'function_name' => $tool,
'filter' => $filter,
]);

$this->addToolsToMessage($message, $functionDto);
$this->addToolsToMessage($message, $functionDto);

$response = StandardsChecker::handle($message);
$this->handleResponse($response, $chat, $message);
$this->response = $response->content;
$this->requiresFollowup = $response->requires_follow_up_prompt;
$this->requiresFollowUp($message->getLatestMessages(), $chat);
}
$toolClass = app()->make($tool);

$response = $toolClass->handle($message);
$this->handleResponse($response, $chat, $message);
$this->response = $response->content;
$this->requiresFollowup = $response->requires_follow_up_prompt;
$this->requiresFollowUp($message->getLatestMessages(), $chat);

} else {
/**
Expand Down
3 changes: 1 addition & 2 deletions Modules/LlmDriver/tests/Feature/ClaudeClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,8 @@ public function test_get_functions(): void
$first = $response[0];
$this->assertArrayHasKey('name', $first);
$this->assertArrayHasKey('input_schema', $first);
$expected = get_fixture('claude_client_get_functions.json');

$this->assertEquals($expected, $response);
$this->assertNotEmpty($response);
}

public function test_functions_prompt(): void
Expand Down
52 changes: 52 additions & 0 deletions app/Domains/Prompts/ReportBuildingFindRequirementsPrompt.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace App\Domains\Prompts;

use Illuminate\Support\Facades\Log;

class ReportBuildingFindRequirementsPrompt
{
public static function prompt(string $context, string $userPrompt, string $collectionDescription): string
{
Log::info('[LaraChain] - ReportBuildingPrompt');

return <<<PROMPT
**Role**
You are a report builder. There are multiple steps to this process. This step will be
taking a page and finding all the requirements for the report.
**Task**
Use the CONTEXT as the page that as the requests of the report. It is one page in many.
Then pull out each requirement so that the results can be used in the next step.
**Format**
The results should be text as paragraphs. Each paragraph should be a requirement.
The results will be passed to the next step. All of this as a JSON array of objects.
** Example **
[
{
"title": "[REQUEST 1 TITLE]",
"content": "[REQUEST 1 CONTENT]"
},
{
"title": "[REQUEST 2 TITLE]",
"content": "[REQUEST 2 CONTENT]"
}
]
** End Example **
** User Prompt **
$userPrompt
$collectionDescription
** Standards **
$context
PROMPT;
}
}
3 changes: 3 additions & 0 deletions app/Http/Middleware/HandleInertiaRequests.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use Illuminate\Http\Request;
use Inertia\Middleware;
use Laravel\Pennant\Feature;
use LlmLaraHub\LlmDriver\LlmDriverClient;
use LlmLaraHub\LlmDriver\LlmDriverFacade;

class HandleInertiaRequests extends Middleware
{
Expand Down Expand Up @@ -46,6 +48,7 @@ public function share(Request $request): array
'app_name' => config('app.name'),
'domain' => config('llmlarahub.domain'),
'features' => Feature::all(),
'tools' => LlmDriverFacade::getFunctionsForUi(),
]);
}
}
11 changes: 11 additions & 0 deletions app/Models/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,17 @@ public function getChat(): ?Chat
return $this->chat;
}

public function getReferenceCollection() : ?Collection
{
$id = data_get($this->meta_data, 'reference_collection_id', null);

if($id) {
return Collection::find($id);
}

return null;
}

public function reRun(): void
{
$assistantResponse = $this;
Expand Down
24 changes: 24 additions & 0 deletions app/Models/Section.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Section extends Model
{
use HasFactory;

protected $guarded = [];

public function document(): BelongsTo
{
return $this->belongsTo(Document::class);
}

public function report(): BelongsTo
{
return $this->belongsTo(Report::class);
}
}
30 changes: 30 additions & 0 deletions database/factories/SectionFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace Database\Factories;

use App\Models\Document;
use App\Models\Report;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Section>
*/
class SectionFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'subject' => $this->faker->sentence(),
'content' => $this->faker->paragraph(),
'response' => $this->faker->paragraph(),
'sort_order' => $this->faker->numberBetween(0, 100),
'document_id' => Document::factory(),
'report_id' => Report::factory(),
];
}
}
Loading

0 comments on commit 12d6611

Please sign in to comment.