Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion app/Integrations/Ollama/Requests/ChatRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,15 @@ public function resolveEndpoint(): string

public function defaultBody(): array
{
$assistant = $this->thread->project->assistant;
$assistant = $this->thread->assistant;

if (!$assistant) {
throw new \Exception('No assistant associated with this thread.');
}

if (!$assistant->model) {
throw new \Exception('The assistant does not have a model specified.');
}

$body = [
'model' => $assistant->model,
Expand Down
10 changes: 9 additions & 1 deletion app/Integrations/OpenAI/Requests/ChatRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ public function resolveEndpoint(): string

public function defaultBody(): array
{
$assistant = $this->thread->project->assistant;
$assistant = $this->thread->assistant;

if (!$assistant) {
throw new \Exception('No assistant associated with this thread.');
}

if (!$assistant->model) {
throw new \Exception('The assistant does not have a model specified.');
}

return [
'model' => $assistant->model,
Expand Down
6 changes: 6 additions & 0 deletions app/Models/Assistant.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

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

class Assistant extends Model
{
Expand All @@ -26,4 +27,9 @@ public function threads()
{
return $this->hasMany(Thread::class);
}

public function projects(): BelongsToMany
{
return $this->belongsToMany(Project::class);
}
}
7 changes: 3 additions & 4 deletions app/Models/Project.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

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

class Project extends Model
Expand All @@ -13,12 +13,11 @@ class Project extends Model

protected $fillable = [
'path',
'assistant_id',
];

public function assistant(): BelongsTo
public function assistants(): BelongsToMany
{
return $this->belongsTo(Assistant::class);
return $this->belongsToMany(Assistant::class);
}

public function threads(): HasMany
Expand Down
105 changes: 78 additions & 27 deletions app/Services/ChatAssistant.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,35 +55,38 @@ public function getCurrentProject(bool $isNew): Project
$projectPath = getcwd();
$project = Project::where('path', $projectPath)->first();

if ($isNew && $project) {
// Update the existing project if isNew is true
$project->assistant_id = $this->createNewAssistant()->id; // Update based on new assistant
$project->save();
return $project;
}

if (!$project) {
// If there's no existing project, create a new one
$userChoice = select(
label: 'No project found. Would you like to create a new assistant or use an existing one?',
options: [
'create_new' => 'Create New Assistant',
'use_existing' => 'Use Existing Assistant',
]
);

$assistantId = match ($userChoice) {
'create_new' => $this->createNewAssistant()->id,
'use_existing' => $this->selectExistingAssistant(),
default => throw new Exception('Invalid choice'),
};

return Project::create([
$project = Project::create([
'path' => $projectPath,
'assistant_id' => $assistantId,
]);
}

if ($isNew) {
// Create a new assistant and associate it with the project
$assistant = $this->createNewAssistant();
$project->assistants()->attach($assistant->id);
} else {
// If not new, check if the project has any assistants
if ($project->assistants()->count() === 0) {
$userChoice = select(
label: 'No assistants found for this project. Would you like to create a new assistant or use an existing one?',
options: [
'create_new' => 'Create New Assistant',
'use_existing' => 'Use Existing Assistant',
]
);

$assistant = match ($userChoice) {
'create_new' => $this->createNewAssistant(),
'use_existing' => Assistant::find($this->selectExistingAssistant()),
default => throw new Exception('Invalid choice'),
};

$project->assistants()->attach($assistant->id);
}
}

return $project;
}

Expand All @@ -96,6 +99,7 @@ public function createNewAssistant(): Assistant
{
$path = getcwd();
$folderName = basename($path);
$project = Project::where('path', $path)->first();

$service = $this->selectService();
$this->ensureAPIKey($service);
Expand Down Expand Up @@ -134,20 +138,37 @@ public function createNewAssistant(): Assistant
public function createThread(bool $isNew): \App\Models\Thread
{
$project = $this->getCurrentProject($isNew);
$latestThread = $project->threads()->latest()->first();

// Select or create an assistant for the thread
$assistant = $this->selectAssistantForProject($project);

$latestThread = $project->threads()
->where('assistant_id', $assistant->id)
->with('assistant') // Eager load the assistant
->latest()
->first();

if ($latestThread && $this->shouldUseExistingThread()) {
return $latestThread;
}

$thread = spin(
fn () => $project->threads()->create([
'assistant_id' => $project->assistant_id,
'assistant_id' => $assistant->id,
'project_id' => $project->id,
'title' => 'New Thread',
]),
'Creating New Thread...'
);

// Ensure the thread is associated with both project and assistant
$thread->load('assistant', 'project');

// Double-check that the assistant is loaded
if (!$thread->assistant) {
throw new \Exception('Failed to load assistant for the thread.');
}

render(view('assistant', [
'answer' => 'How can I help you?',
]));
Expand Down Expand Up @@ -198,13 +219,13 @@ private function handleTools($thread, $message): string
'content' => $message->content,
];

if (!empty($message->tool_calls)) {
if ($this->hasToolCalls($message)) {
$messageData['tool_calls'] = $message->tool_calls;
}

$thread->messages()->create($messageData);

if (!empty($message->tool_calls)) {
if ($this->hasToolCalls($message)) {
$this->renderAnswer($answer);

foreach ($message->tool_calls as $toolCall) {
Expand All @@ -217,6 +238,11 @@ private function handleTools($thread, $message): string
return $answer;
}

private function hasToolCalls($message): bool
{
return $message->tool_calls && $message->tool_calls->isNotEmpty();
}

private function selectService(): string
{
return select(
Expand Down Expand Up @@ -323,4 +349,29 @@ private function ensureAPIKey(string $service): void
$this->onBoardingSteps->requestAPIKey($service);
}
}

private function selectAssistantForProject(Project $project): Assistant
{
$projectAssistants = $project->assistants;

if ($projectAssistants->isEmpty()) {
return $this->createNewAssistant();
}

$options = $projectAssistants->pluck('name', 'id')->toArray();
$options['create_new'] = 'Create New Assistant';

$selectedOption = select(
label: 'Select an assistant for this project:',
options: $options
);

if ($selectedOption === 'create_new') {
$newAssistant = $this->createNewAssistant();
$project->assistants()->attach($newAssistant->id);
return $newAssistant;
}

return Assistant::find($selectedOption);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up(): void
{
Schema::table('projects', function (Blueprint $table) {
$table->dropForeign(['assistant_id']);
$table->dropColumn('assistant_id');
});

Schema::create('assistant_project', function (Blueprint $table) {
$table->id();
$table->foreignId('assistant_id')->constrained()->onDelete('cascade');
$table->foreignId('project_id')->constrained()->onDelete('cascade');
$table->timestamps();

$table->unique(['assistant_id', 'project_id']);
});
}

public function down(): void
{
Schema::dropIfExists('assistant_project');

Schema::table('projects', function (Blueprint $table) {
$table->foreignId('assistant_id')->constrained('assistants');
});
}
};