Skip to content

Commit f20075d

Browse files
committed
update console
1 parent 8e4dab8 commit f20075d

File tree

6 files changed

+807
-205
lines changed

6 files changed

+807
-205
lines changed

src/Agents/Prebuilt/WeatherAgent.php

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,27 @@ public static function name(): string
2727
public function prompt(): ChatPromptTemplate|ChatPromptBuilder|string
2828
{
2929
return Cortex::prompt([
30-
new SystemMessage('You are a weather agent. Output in sentences.'),
31-
new UserMessage('What is the weather in {location}?'),
30+
new SystemMessage(<<<INSTRUCTIONS
31+
You are a helpful weather assistant that provides accurate weather information and can help planning activities based on the weather.
32+
Your primary function is to help users get weather details for specific locations. When responding:
33+
- Always ask for a location if none is provided
34+
- If the location name isn't in English, please translate it
35+
- If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York")
36+
- Include relevant details like humidity, wind conditions, and precipitation
37+
- Keep responses concise but informative
38+
- If the user asks for activities and provides the weather forecast, suggest activities based on the weather forecast.
39+
- If the user asks for activities, respond in the format they request.
40+
41+
Use the get_weather tool to fetch current weather data.
42+
INSTRUCTIONS
43+
),
3244
]);
3345
}
3446

3547
public function llm(): LLM|string|null
3648
{
37-
// return Cortex::llm('openai_responses', 'gpt-5-mini')->ignoreFeatures();
3849
// return Cortex::llm('ollama', 'gpt-oss:20b')->ignoreFeatures();
39-
return Cortex::llm('openai', 'gpt-4o-mini')->ignoreFeatures();
50+
return Cortex::llm('openai', 'gpt-4.1-mini')->ignoreFeatures();
4051
}
4152

4253
#[Override]
@@ -46,12 +57,4 @@ public function tools(): array|ToolKit
4657
OpenMeteoWeatherTool::class,
4758
];
4859
}
49-
50-
public function output(): ObjectSchema|string|null
51-
{
52-
return Schema::object()->properties(
53-
Schema::string('location')->required(),
54-
Schema::string('summary')->required(),
55-
);
56-
}
5760
}

src/Console/AgentChat.php

Lines changed: 16 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44

55
namespace Cortex\Console;
66

7-
use Throwable;
87
use Cortex\Cortex;
98
use Cortex\Agents\Agent;
109
use InvalidArgumentException;
11-
use Cortex\LLM\Enums\ChunkType;
1210
use Illuminate\Console\Command;
1311
use Cortex\Facades\AgentRegistry;
14-
use Cortex\LLM\Data\ChatGenerationChunk;
15-
use Cortex\LLM\Data\Messages\UserMessage;
12+
13+
use function Laravel\Prompts\info;
14+
use function Laravel\Prompts\outro;
15+
use function Laravel\Prompts\table;
16+
use function Laravel\Prompts\error as promptsError;
1617

1718
class AgentChat extends Command
1819
{
@@ -21,7 +22,7 @@ class AgentChat extends Command
2122
*
2223
* @var string
2324
*/
24-
protected $signature = 'cortex:chat {agent : The name of the agent to chat with}';
25+
protected $signature = 'cortex:chat {agent : The name of the agent to chat with} {--debug : Show debug information including tool calls}';
2526

2627
/**
2728
* The console command description.
@@ -41,204 +42,32 @@ public function handle(): int
4142
return self::FAILURE;
4243
}
4344

44-
$this->info('Chatting with agent: ' . $agent->getName());
45-
$this->line("Type 'exit' or 'quit' to end the conversation.\n");
46-
47-
while (true) {
48-
$userInput = $this->ask('You');
49-
50-
if ($userInput === null || in_array(strtolower(trim($userInput)), ['exit', 'quit', 'q'], true)) {
51-
$this->info('Goodbye!');
52-
break;
53-
}
54-
55-
if (trim($userInput) === '') {
56-
continue;
57-
}
45+
// Create and start the TUI
46+
$chatPrompt = new ChatPrompt($agent, $this->option('debug'));
47+
$chatPrompt->prompt();
5848

59-
try {
60-
$this->line("\nAgent:");
61-
$this->streamAgentResponse($agent, new UserMessage($userInput));
62-
$this->newLine();
63-
} catch (Throwable $e) {
64-
$this->renderErrorMessage($e->getMessage());
65-
66-
return self::FAILURE;
67-
}
68-
}
49+
outro('Goodbye!');
6950

7051
return self::SUCCESS;
7152
}
7253

73-
/**
74-
* Stream the agent's response and display it in real-time.
75-
*/
76-
protected function streamAgentResponse(Agent $agent, UserMessage $userMessage): void
77-
{
78-
$result = $agent->stream(messages: [$userMessage]);
79-
80-
$lastContentLength = 0;
81-
82-
foreach ($result as $chunk) {
83-
// Handle different chunk types
84-
match ($chunk->type) {
85-
ChunkType::TextDelta => $this->handleTextDelta($chunk, $lastContentLength),
86-
ChunkType::TextStart => $this->handleTextStart(),
87-
ChunkType::TextEnd => $this->handleTextEnd(),
88-
ChunkType::ToolInputStart => $this->handleToolInputStart($chunk),
89-
ChunkType::ToolInputEnd => $this->handleToolInputEnd($chunk),
90-
ChunkType::ToolOutputEnd => $this->handleToolOutputEnd($chunk),
91-
ChunkType::StepStart => $this->handleStepStart(),
92-
ChunkType::StepEnd => $this->handleStepEnd(),
93-
ChunkType::RunStart => $this->handleRunStart(),
94-
ChunkType::RunEnd => $this->handleRunEnd(),
95-
ChunkType::Error => $this->handleError($chunk),
96-
default => null,
97-
};
98-
99-
// Update last displayed length from contentSoFar for final chunks
100-
if ($chunk->isFinal && $chunk->contentSoFar !== '') {
101-
$lastContentLength = strlen($chunk->contentSoFar);
102-
}
103-
}
104-
}
105-
106-
/**
107-
* Handle text delta chunks - display incremental text updates.
108-
*/
109-
protected function handleTextDelta(ChatGenerationChunk $chunk, int &$lastContentLength): void
110-
{
111-
// Use contentSoFar to get the cumulative text and display only the new part
112-
if ($chunk->contentSoFar !== '') {
113-
$newText = substr($chunk->contentSoFar, $lastContentLength);
114-
115-
if ($newText !== '') {
116-
$this->output->write($newText);
117-
$lastContentLength = strlen($chunk->contentSoFar);
118-
}
119-
} else {
120-
// Fallback to text() if contentSoFar is not available
121-
$text = $chunk->text();
122-
123-
if ($text !== null && $text !== '') {
124-
$this->output->write($text);
125-
}
126-
}
127-
}
128-
129-
/**
130-
* Handle text start chunks.
131-
*/
132-
protected function handleTextStart(): void
133-
{
134-
// Text start - no visual output needed
135-
}
136-
137-
/**
138-
* Handle text end chunks.
139-
*/
140-
protected function handleTextEnd(): void
141-
{
142-
// Text end - ensure newline
143-
$this->newLine();
144-
}
145-
146-
/**
147-
* Handle tool input start chunks.
148-
*/
149-
protected function handleToolInputStart(ChatGenerationChunk $chunk): void
150-
{
151-
$toolCalls = $chunk->message->toolCalls;
152-
153-
if ($toolCalls === null || $toolCalls->isEmpty()) {
154-
return;
155-
}
156-
157-
$this->newLine();
158-
$this->line('<fg=cyan>🔧 Calling tools:</fg=cyan>');
159-
foreach ($toolCalls as $toolCall) {
160-
$this->line(sprintf(' - <fg=yellow>%s</fg=yellow>', $toolCall->function->name));
161-
}
162-
}
163-
164-
/**
165-
* Handle tool input end chunks.
166-
*/
167-
protected function handleToolInputEnd(ChatGenerationChunk $chunk): void
168-
{
169-
// Tool input complete - already displayed in start
170-
}
171-
172-
/**
173-
* Handle tool output end chunks.
174-
*/
175-
protected function handleToolOutputEnd(ChatGenerationChunk $chunk): void
176-
{
177-
$this->line('<fg=green>✓ Tool execution complete</fg=green>');
178-
}
179-
180-
/**
181-
* Handle step start chunks.
182-
*/
183-
protected function handleStepStart(): void
184-
{
185-
// Step start - no visual output needed for now
186-
}
187-
188-
/**
189-
* Handle step end chunks.
190-
*/
191-
protected function handleStepEnd(): void
192-
{
193-
// Step end - no visual output needed
194-
}
195-
196-
/**
197-
* Handle run start chunks.
198-
*/
199-
protected function handleRunStart(): void
200-
{
201-
// Run start - no visual output needed
202-
}
203-
204-
/**
205-
* Handle run end chunks.
206-
*/
207-
protected function handleRunEnd(): void
208-
{
209-
// Run end - no visual output needed
210-
}
211-
212-
/**
213-
* Handle error chunks.
214-
*/
215-
protected function handleError(ChatGenerationChunk $chunk): void
216-
{
217-
$this->renderErrorMessage($chunk->exception?->getMessage() ?? 'An error occurred');
218-
}
219-
220-
protected function renderErrorMessage(string $message): void
221-
{
222-
$this->newLine();
223-
$this->error('Error: ' . $message);
224-
}
225-
22654
protected function getAgent(): ?Agent
22755
{
22856
$agentName = $this->argument('agent');
22957

23058
try {
23159
return Cortex::agent($agentName);
23260
} catch (InvalidArgumentException) {
233-
$this->error(sprintf("Agent '%s' not found in registry.", $agentName));
61+
promptsError(sprintf("Agent '%s' not found in registry.", $agentName));
23462

23563
$availableAgents = AgentRegistry::names();
23664

23765
if (! empty($availableAgents)) {
238-
$this->info('Available agents:');
239-
foreach ($availableAgents as $name) {
240-
$this->line(' - ' . $name);
241-
}
66+
info('Available agents:');
67+
table(
68+
headers: ['Name'],
69+
rows: array_map(fn(string $name): array => [$name], $availableAgents),
70+
);
24271
}
24372

24473
return null;

0 commit comments

Comments
 (0)