Skip to content

Commit

Permalink
Merge pull request #29 from bootstrapguru/enhance-ollama-tool-use
Browse files Browse the repository at this point in the history
Enhance Tool Call Argument Handling for Ollama Integration
  • Loading branch information
vijaythecoder authored Jul 23, 2024
2 parents 2e394e4 + e6a8284 commit c4ca06a
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 18 deletions.
106 changes: 97 additions & 9 deletions app/Integrations/Ollama/Requests/ChatRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
namespace App\Integrations\Ollama\Requests;

use App\Data\MessageData;
use App\Data\ToolCallData;
use App\Data\ToolFunctionData;
use App\Models\Thread;
use Illuminate\Support\Str;
use JsonException;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
Expand Down Expand Up @@ -31,27 +34,84 @@ public function defaultBody(): array
{
$assistant = $this->thread->project->assistant;

return [
$body = [
'model' => $assistant->model,
'messages' => $this->formatMessages($assistant),
'stream' => false,
'raw' => true,
'tools' => array_values($this->tools),
'tools' => $this->formatTools(),
];


return $body;
}

private function formatMessages($assistant): array
{
$systemMessage = [
'role' => 'system',
'content' => sprintf(
'[INST]%s[/INST] [AVAILABLE_TOOLS]%s[/AVAILABLE_TOOLS]',
$assistant->prompt,
json_encode(array_values($this->tools))
),
'content' => $assistant->prompt,
];

return [$systemMessage, ...$this->thread->messages->toArray()];
$formattedMessages = [$systemMessage];

foreach ($this->thread->messages as $message) {
$formattedMessage = [
'role' => $message['role'],
'content' => $message['content'],
];

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

$formattedMessages[] = $formattedMessage;
}

return $formattedMessages;
}

private function formatToolCalls($toolCalls): array
{
return array_map(function ($toolCall) {
$function = $toolCall['function'];

// Ensure arguments is a JSON object string
$arguments = is_string($function['arguments'])
? json_decode($function['arguments'], true) // Decode string to ensure it's valid JSON
: json_encode($function['arguments'], JSON_FORCE_OBJECT); // Encode array/object to JSON object

return [
'id' => $toolCall['id'],
'type' => 'function',
'function' => [
'name' => $function['name'],
'arguments' => $arguments, // Always a JSON object
],
];
}, $toolCalls);
}

private function formatTools(): array
{

$formattedTools = [];
foreach ($this->tools as $key => $tool) {
if (is_array($tool) && isset($tool['function'])) {
$formattedTools[] = [
'type' => 'function',
'function' => [
'name' => $tool['function']['name'] ?? $key,
'description' => $tool['function']['description'] ?? '',
'parameters' => $tool['function']['parameters'] ?? [],
],
];
} else {
}
}


return $formattedTools;
}

/**
Expand All @@ -60,6 +120,34 @@ private function formatMessages($assistant): array
public function createDtoFromResponse(Response $response): MessageData
{
$data = $response->json();
return MessageData::from($data['message'] ?? []);
$message = $data['message'] ?? [];
$tools = collect();

if (isset($message['tool_calls'])) {
foreach ($message['tool_calls'] as $toolCall) {
$arguments = $toolCall['function']['arguments'];
// Ensure arguments is a JSON string
if (is_array($arguments)) {
$arguments = json_encode($arguments, JSON_FORCE_OBJECT);
} elseif (!is_string($arguments)) {
$arguments = json_encode([$arguments], JSON_FORCE_OBJECT);
}

$fn = ToolFunctionData::from([
'name' => $toolCall['function']['name'],
'arguments' => $arguments
]);

$tools->push(ToolCallData::from([
'id' => $toolCall['id'] ?? 'ollama-'.Str::random(10),
'type' => 'function',
'function' => $fn
]));
}

$message['tool_calls'] = $tools;
}

return MessageData::from($message);
}
}
27 changes: 22 additions & 5 deletions app/Services/ChatAssistant.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,18 @@ private function handleTools($thread, $message): string
{
$answer = $message->content;

$thread->messages()->create($message->toArray());
if ($message->tool_calls !== null && $message->tool_calls->isNotEmpty()) {
$messageData = [
'role' => $message->role,
'content' => $message->content,
];

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

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

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

foreach ($message->tool_calls as $toolCall) {
Expand Down Expand Up @@ -292,9 +302,16 @@ private function executeToolCall($thread, $toolCall): void

$thread->messages()->create([
'role' => 'tool',
'tool_call_id' => $toolCall->id,
'name' => $toolCall->function->name,
'content' => $toolResponse,
'tool_calls' => [
[
'id' => $toolCall->id,
'function' => [
'name' => $toolCall->function->name,
'arguments' => $toolCall->function->arguments,
],
],
],
]);
} catch (Exception $e) {
throw new Exception("Error calling tool: {$e->getMessage()}");
Expand All @@ -308,4 +325,4 @@ private function ensureAPIKey(string $service): void
$this->onBoardingSteps->requestAPIKey($service);
}
}
}
}
17 changes: 13 additions & 4 deletions app/Services/Request/ChatRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,23 @@ public function defaultBody(): array
$messages = [[
'role' => 'system',
'content' => $assistant->prompt,
],
...$this->thread->messages,
];
]];

foreach ($this->thread->messages as $message) {
$messages[] = [
'role' => $message->role,
'content' => $message->content,
];

if ($message->tool_calls) {
$messages[count($messages) - 1]['tool_calls'] = $message->tool_calls;
}
}

return [
'model' => $assistant->model,
'messages' => $messages,
'tools' => array_values($this->tools),
];
}
}
}
3 changes: 3 additions & 0 deletions app/Utils/OnBoardingSteps.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ private function configurationFileExists(): bool
return true;
}

/**
* @throws Exception
*/
public function requestAPIKey(string $service): string
{
$apiKey = password(
Expand Down
8 changes: 8 additions & 0 deletions config/database.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
],

'sqlite-dev' => [
'driver' => 'sqlite',
'url' => env('DB_URL'),
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
],

'mysql' => [
'driver' => 'mysql',
'url' => env('DB_URL'),
Expand Down

0 comments on commit c4ca06a

Please sign in to comment.