Skip to content

Commit 83fe67b

Browse files
committed
wip
1 parent 8a7f845 commit 83fe67b

File tree

8 files changed

+123
-21
lines changed

8 files changed

+123
-21
lines changed

config/cortex.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -337,12 +337,12 @@
337337
'ignore_features' => env('CORTEX_MODEL_INFO_IGNORE_FEATURES', false),
338338

339339
'providers' => [
340-
// OllamaModelInfoProvider::class => [
341-
// 'host' => env('OLLAMA_BASE_URI', 'http://localhost:11434'),
342-
// ],
343-
// LMStudioModelInfoProvider::class => [
344-
// 'host' => env('LMSTUDIO_BASE_URI', 'http://localhost:1234'),
345-
// ],
340+
OllamaModelInfoProvider::class => [
341+
'host' => env('OLLAMA_BASE_URI', 'http://localhost:11434'),
342+
],
343+
LMStudioModelInfoProvider::class => [
344+
'host' => env('LMSTUDIO_BASE_URI', 'http://localhost:1234'),
345+
],
346346
LiteLLMModelInfoProvider::class => [
347347
'host' => env('LITELLM_BASE_URI'),
348348
'apiKey' => env('LITELLM_API_KEY'),

src/Cortex.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Cortex\Facades\AgentRegistry;
1414
use Cortex\Prompts\Contracts\PromptBuilder;
1515
use Cortex\LLM\Contracts\LLM as LLMContract;
16+
use Cortex\Prompts\Contracts\PromptTemplate;
1617
use Cortex\Agents\Prebuilt\GenericAgentBuilder;
1718
use Cortex\LLM\Data\Messages\MessageCollection;
1819
use Cortex\Embeddings\Contracts\Embeddings as EmbeddingsContract;
@@ -24,18 +25,26 @@ class Cortex
2425
*
2526
* @param \Cortex\LLM\Data\Messages\MessageCollection|array<int, \Cortex\LLM\Contracts\Message|\Cortex\LLM\Data\Messages\MessagePlaceholder>|string|null $messages
2627
*
27-
* @return ($messages is null ? \Cortex\Prompts\Prompt : ($messages is string ? \Cortex\Prompts\Builders\TextPromptBuilder : \Cortex\Prompts\Builders\ChatPromptBuilder))
28+
* @return ($messages is null ? \Cortex\Prompts\Prompt : ($messages is string ? \Cortex\Prompts\Builders\TextPromptBuilder|\Cortex\Prompts\Contracts\PromptTemplate : \Cortex\Prompts\Builders\ChatPromptBuilder))
2829
*/
2930
public static function prompt(
3031
MessageCollection|array|string|null $messages = null,
31-
): Prompt|PromptBuilder {
32+
): Prompt|PromptBuilder|PromptTemplate {
3233
if (func_num_args() === 0) {
3334
return new Prompt();
3435
}
3536

36-
return is_string($messages)
37-
? Prompt::builder('text')->text($messages)
38-
: Prompt::builder('chat')->messages($messages);
37+
if (is_string($messages)) {
38+
if (Utils::isPromptShortcut($messages)) {
39+
['factory' => $factory, 'driver' => $driver] = Utils::splitPromptShortcut($messages);
40+
41+
return Prompt::factory($driver)->make($factory);
42+
}
43+
44+
return Prompt::builder('text')->text($messages);
45+
}
46+
47+
return Prompt::builder('chat')->messages($messages);
3948
}
4049

4150
/**

src/Prompts/PromptFactoryManager.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function createBladeDriver(): BladePromptFactory
4646
$config = $this->config->get('cortex.prompt_factory.blade');
4747

4848
return new BladePromptFactory(
49-
base_path($config['path'] ?? 'resources/views/prompts'),
49+
$config['path'] ?? base_path('resources/views/prompts'),
5050
);
5151
}
5252
}

src/Support/Utils.php

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,7 @@ public static function toMessageCollection(MessageCollection|Message|array|strin
9595
*/
9696
public static function isLLMShortcut(string $input): bool
9797
{
98-
$values = Str::of($input)->explode(self::SHORTCUT_SEPARATOR, 2);
99-
100-
return $values->count() > 1
101-
&& config(sprintf('cortex.llm.%s', $values->first())) !== null;
98+
return self::isShortcut($input, 'cortex.llm.%s');
10299
}
103100

104101
/**
@@ -122,6 +119,35 @@ public static function splitLLMShortcut(string $input): array
122119
];
123120
}
124121

122+
/**
123+
* Determine if the given string is a prompt factory shortcut.
124+
*/
125+
public static function isPromptShortcut(string $input): bool
126+
{
127+
return self::isShortcut($input, 'cortex.prompt_factory.%s');
128+
}
129+
130+
/**
131+
* Split the given prompt factory shortcut into factory and driver.
132+
*
133+
* @return array{factory: string, driver: string|null}
134+
*/
135+
public static function splitPromptShortcut(string $input): array
136+
{
137+
$split = Str::of($input)->explode(self::SHORTCUT_SEPARATOR, 2);
138+
139+
$factory = $split->first();
140+
141+
$driver = $split->count() === 1
142+
? null
143+
: $split->last();
144+
145+
return [
146+
'factory' => $factory,
147+
'driver' => $driver,
148+
];
149+
}
150+
125151
/**
126152
* Convert the given provider to an LLM instance.
127153
*/
@@ -227,4 +253,15 @@ public static function resolveMimeType(string $value): string
227253

228254
throw new ContentException('Invalid content.');
229255
}
256+
257+
/**
258+
* Determine if the given string is a shortcut.
259+
*/
260+
protected static function isShortcut(string $input, string $configPath): bool
261+
{
262+
$values = Str::of($input)->explode(self::SHORTCUT_SEPARATOR, 2);
263+
264+
return $values->count() > 1
265+
&& config(sprintf($configPath, $values->first())) !== null;
266+
}
230267
}

tests/TestCase.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Orchestra\Testbench\TestCase as BaseTestCase;
1010
use Orchestra\Testbench\Concerns\InteractsWithPest;
1111

12+
use function Orchestra\Testbench\package_path;
13+
1214
abstract class TestCase extends BaseTestCase
1315
{
1416
use WithWorkbench;
@@ -20,4 +22,9 @@ protected function defineEnvironment($app)
2022
$config->set('cortex.model_info.ignore_features', true);
2123
});
2224
}
25+
26+
public static function applicationBasePath()
27+
{
28+
return package_path('workbench');
29+
}
2330
}

tests/Unit/Support/UtilsTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,37 @@
4949
]);
5050
});
5151

52+
describe('isPromptShortcut()', function (): void {
53+
test('it can detect a prompt shortcut', function (string $input, bool $expected): void {
54+
expect(Utils::isPromptShortcut($input))->toBe($expected);
55+
})->with([
56+
'mcp/driver' => [
57+
'input' => 'mcp/driver',
58+
'expected' => true,
59+
],
60+
'blade/template' => [
61+
'input' => 'blade/template',
62+
'expected' => true,
63+
],
64+
'langfuse/prompt' => [
65+
'input' => 'langfuse/prompt',
66+
'expected' => true,
67+
],
68+
'invalid-factory/driver' => [
69+
'input' => 'invalid-factory/driver',
70+
'expected' => false,
71+
],
72+
'no-separator' => [
73+
'input' => 'mcp',
74+
'expected' => false,
75+
],
76+
'empty-string' => [
77+
'input' => '',
78+
'expected' => false,
79+
],
80+
]);
81+
});
82+
5283
describe('llm()', function (): void {
5384
test('can convert string to llm', function (string $input, string $instance, ModelProvider $provider, string $model): void {
5485
expect(Utils::isLLMShortcut($input))->toBeTrue();

workbench/app/Providers/CortexServiceProvider.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
use Cortex\Agents\Agent;
77
use Cortex\JsonSchema\Schema;
88
use Illuminate\Support\ServiceProvider;
9+
use Cortex\ModelInfo\Enums\ModelFeature;
910
use Cortex\LLM\Data\Messages\UserMessage;
1011
use Cortex\LLM\Data\Messages\SystemMessage;
1112

13+
use function Orchestra\Testbench\package_path;
14+
1215
class CortexServiceProvider extends ServiceProvider
1316
{
1417
/**
@@ -24,10 +27,15 @@ public function register(): void
2427
*/
2528
public function boot(): void
2629
{
30+
config()->set(
31+
'cortex.prompt_factory.blade.path',
32+
package_path('workbench/resources/views/prompts'),
33+
);
34+
2735
Cortex::registerAgent(new Agent(
2836
name: 'holiday_generator',
2937
prompt: 'Invent a new holiday and describe its traditions. Max 3 sentences.',
30-
llm: Cortex::llm('openai', 'gpt-4o-mini')->withTemperature(1.5),
38+
llm: Cortex::llm('ollama/gpt-oss:20b')->withTemperature(1.5),
3139
output: [
3240
Schema::string('name')->required(),
3341
Schema::string('description')->required(),
@@ -55,8 +63,11 @@ public function boot(): void
5563
new UserMessage('Tell me a joke about {topic}.'),
5664
])
5765
->metadata(
58-
provider: 'ollama',
59-
model: 'phi4',
66+
provider: 'lmstudio',
67+
model: 'gpt-oss:20b',
68+
parameters: [
69+
'temperature' => 1.5,
70+
],
6071
structuredOutput: Schema::object()->properties(
6172
Schema::string('setup')->required(),
6273
Schema::string('punchline')->required(),
@@ -79,8 +90,15 @@ public function boot(): void
7990
Cortex::registerAgent(new Agent(
8091
name: 'generic',
8192
prompt: 'You are a helpful assistant.',
82-
// llm: 'ollama/gpt-oss:20b',
8393
llm: 'lmstudio/openai/gpt-oss-20b'
8494
));
95+
96+
Cortex::registerAgent(new Agent(
97+
name: 'code_generator',
98+
prompt: Cortex::prompt()->factory('blade')->make('example', [
99+
'name' => 'Alice',
100+
'language' => 'Python',
101+
]),
102+
));
85103
}
86104
}

workbench/resources/views/prompts/example.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
]);
1313
1414
// Configure the LLM
15-
llm('openai', 'gpt-4', [
15+
llm('lmstudio', 'openai/gpt-oss-20b', [
1616
'temperature' => 0.7,
1717
'max_tokens' => 1000,
1818
]);

0 commit comments

Comments
 (0)