Skip to content

Commit

Permalink
Evals + bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ddebowczyk committed Oct 10, 2024
1 parent b082abf commit 066a205
Show file tree
Hide file tree
Showing 26 changed files with 493 additions and 297 deletions.
1 change: 1 addition & 0 deletions config/debug.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
'http' => [
'enabled' => false,
'trace' => false,
'requestUrl' => true,
'requestHeaders' => true,
'requestBody' => true,
'responseHeaders' => true,
Expand Down
40 changes: 26 additions & 14 deletions evals/LLMModes/run.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,26 @@
$loader->add('Cognesy\\Evals\\', __DIR__ . '../../evals/');

use Cognesy\Instructor\Enums\Mode;
use Cognesy\Instructor\Extras\Evals\Combination;
use Cognesy\Instructor\Extras\Evals\Data\EvalInput;
use Cognesy\Instructor\Extras\Evals\Data\EvalSchema;
use Cognesy\Instructor\Extras\Evals\Evaluator;
use Cognesy\Instructor\Extras\Evals\Inference\RunInference;
use Cognesy\Instructor\Extras\Evals\Mappings\ConnectionModes;
use Cognesy\Instructor\Utils\Str;

$connections = [
// 'azure',
// 'cohere1',
// 'cohere2',
// 'fireworks',
// 'gemini',
'azure',
'cohere1',
'cohere2',
'fireworks',
'gemini',
'groq',
// 'mistral',
// 'ollama',
// 'openai',
// 'openrouter',
// 'together',
'mistral',
'ollama',
'openai',
'openrouter',
'together',
];

$streamingModes = [
Expand All @@ -45,6 +47,15 @@
// azure, Mode::JsonSchema, sync|stream
//

$combinations = Combination::generator(
mapping: ConnectionModes::class,
sources: [
'isStreaming' => $streamingModes,
'mode' => $modes,
'connection' => $connections,
],
);

function evalFn(EvalInput $er) {
$decoded = json_decode($er->response->json(), true);
$isCorrect = match($er->mode) {
Expand Down Expand Up @@ -93,12 +104,13 @@ function validateToolsData(array $data) : bool {
['role' => 'user', 'content' => 'What is the name and founding year of our company?'],
],
schema: $schema,
executorClass: RunInference::class,
runner: new RunInference(),
evalFn: fn(EvalInput $evalInput) => evalFn($evalInput),
);

$outputs = $evaluator->execute(
connections: $connections,
modes: $modes,
streamingModes: $streamingModes
// connections: $connections,
// modes: $modes,
// streamingModes: $streamingModes
combinations: $combinations
);
69 changes: 30 additions & 39 deletions evals/SimpleExtraction/run.php
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
<?php

use Cognesy\Instructor\Enums\Mode;
use Cognesy\Instructor\Extras\Evals\Combination;
use Cognesy\Instructor\Extras\Evals\Data\EvalInput;
use Cognesy\Instructor\Extras\Evals\Evaluator;
use Cognesy\Instructor\Extras\Evals\Instructor\RunInstructor;
use Cognesy\Instructor\Extras\Evals\Mappings\ConnectionModes;
use Cognesy\Instructor\Utils\Debug\Debug;

$loader = require 'vendor/autoload.php';
$loader->add('Cognesy\\Instructor\\', __DIR__ . '../../src/');

$connections = [
'azure',
'cohere1',
// 'azure',
// 'cohere1',
'cohere2',
'fireworks',
'gemini',
'groq',
'mistral',
'ollama',
'openai',
'openrouter',
'together',
// 'fireworks',
// 'gemini',
// 'groq',
// 'mistral',
// 'ollama',
// 'openai',
// 'openrouter',
// 'together',
];

$streamingModes = [
Expand All @@ -34,25 +37,34 @@
Mode::Tools,
];

//$report = file_get_contents(__DIR__ . '/report.txt');
//$examples = require 'examples.php';
//$prompt = 'Extract a list of project events with all the details from the provided input in JSON format using schema: <|json_schema|>';
//$responseModel = Sequence::of(ProjectEvent::class);
$combinations = Combination::generator(
mapping: ConnectionModes::class,
sources: [
'isStreaming' => $streamingModes,
'mode' => $modes,
'connection' => $connections,
],
);

class Company {
public string $name;
public int $foundingYear;
}

//Debug::enable();

function evalFn(EvalInput $er) {
/** @var Person $decoded */
$person = $er->response->value();
return $person->name === 'ACME'
&& $person->foundingYear === 2020;
}

//Debug::enable();

//$report = file_get_contents(__DIR__ . '/report.txt');
//$examples = require 'examples.php';
//$prompt = 'Extract a list of project events with all the details from the provided input in JSON format using schema: <|json_schema|>';
//$responseModel = Sequence::of(ProjectEvent::class);

$outputs = (new Evaluator(
messages: [
['role' => 'user', 'content' => 'YOUR GOAL: Use tools to store the information from context based on user questions.'],
Expand All @@ -62,29 +74,8 @@ function evalFn(EvalInput $er) {
['role' => 'user', 'content' => 'What is the name and founding year of our company?'],
],
schema: Company::class,
executorClass: RunInstructor::class,
runner: new RunInstructor(),
evalFn: fn(EvalInput $er) => evalFn($er),
))->execute(
connections: $connections,
modes: $modes,
streamingModes: $streamingModes
combinations: $combinations
);


//$connection = 'gemini';
//$mode = Mode::Json;
//$withStreaming = false;
//
//$action = new ExtractData(
// messages: $input,
// responseModel: $responseModel,
// connection: $connection,
// mode: $mode,
// withStreaming: $withStreaming,
// prompt: $prompt,
// examples: $examples,
// model: 'gemini-1.5-pro',
//);
//
//$response = $action();
//dump($response->get()->all());
2 changes: 1 addition & 1 deletion src/Enums/Mode.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ enum Mode : string
case Tools = 'tool_call';
case Json = 'json';
case JsonSchema = 'json_schema';
case MdJson = 'markdown_json';
case MdJson = 'md_json';
case Text = 'text'; // unstructured text response

public function is(array|Mode $mode) : bool {
Expand Down
99 changes: 99 additions & 0 deletions src/Extras/Evals/Combination.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

namespace Cognesy\Instructor\Extras\Evals;

use ArrayIterator;
use Cognesy\Instructor\Extras\Evals\Contracts\CanMapValues;
use InvalidArgumentException;
use Iterator;
use Generator;

/**
* Class responsible for generating combinations of multiple iterables.
*/
class Combination
{
/**
* Generates all possible combinations of the provided sources and maps them to instances of the mapping class.
*
* @template T
*
* @param class-string<CanMapValues> $mapping The fully qualified class name that implements CanMapValues.
* @param array<string, iterable> $sources Associative array mapping keys to their respective iterables.
*
* @return Generator<int, T> Yields instances of the mapping class for each combination.
*
* @throws InvalidArgumentException If any key in order is missing from sources.
*/
public static function generator(
string $mapping,
array $sources
): Generator {
$order = array_keys($sources);
// Ensure all keys in order exist in sources
foreach ($order as $key) {
if (!array_key_exists($key, $sources)) {
throw new InvalidArgumentException("Source for key '{$key}' not provided.");
}
}

// Initialize iterators for each key in the specified order
$iterators = [];
foreach ($order as $key) {
$iterators[$key] = self::getIterator($sources[$key]);
}

// Start the recursive generation of combinations
yield from self::generateCombinations($mapping, $order, $iterators, []);
}

/**
* Recursively generates combinations of values.
*
* @template T
*
* @param class-string<CanMapValues> $mapping
* @param string[] $keys
* @param array<string, Iterator> $iterators
* @param array<string, mixed> $currentCombination
*
* @return Generator<int, T>
*/
private static function generateCombinations(
string $mapping,
array $keys,
array $iterators,
array $currentCombination
): Generator {
$key = array_shift($keys);

if ($key === null) {
// Base case: all keys have been processed
yield $mapping::map($currentCombination);
return;
}

foreach ($iterators[$key] as $value) {
$newCombination = $currentCombination;
$newCombination[$key] = $value;

// Recursively generate combinations for the remaining keys
yield from self::generateCombinations($mapping, $keys, $iterators, $newCombination);
}
}

/**
* Converts an iterable into an Iterator.
*
* @param iterable $iterable
* @return Iterator
*/
private static function getIterator(iterable $iterable): Iterator
{
if ($iterable instanceof Iterator) {
return $iterable;
}

return new ArrayIterator(array: $iterable);
}
}
13 changes: 7 additions & 6 deletions src/Extras/Evals/Console/Display.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ class Display
{
public function before(Mode $mode, string $connection, bool $isStreamed) : void {
echo Console::columns([
[14, $mode->value, STR_PAD_RIGHT, Color::YELLOW],
[12, $connection, STR_PAD_RIGHT, Color::WHITE],
[10, $isStreamed ? 'stream' : 'sync', STR_PAD_LEFT, $isStreamed ? Color::BLUE : Color::DARK_BLUE],
[10, $connection, STR_PAD_RIGHT, Color::WHITE],
[11, $mode->value, STR_PAD_RIGHT, Color::YELLOW],
[8, $isStreamed ? 'stream' : 'sync', STR_PAD_LEFT, $isStreamed ? Color::BLUE : Color::DARK_BLUE],
], 80);
Console::print('', [Color::GRAY, Color::BG_BLACK]);
}

public function after(EvalOutput $evalResponse) : void {
$answer = $evalResponse->notes;
$answerLine = str_replace("\n", '\n', $answer);
$isCorrect = $evalResponse->isCorrect;
$metric = $evalResponse->metric;
$timeElapsed = $evalResponse->timeElapsed;
$tokensPerSec = $evalResponse->outputTps();
$exception = $evalResponse->exception;
Expand All @@ -34,14 +34,15 @@ public function after(EvalOutput $evalResponse) : void {
//Console::println(, [Color::RED, Color::BG_BLACK]);
echo Console::columns([
[9, '', STR_PAD_LEFT, [Color::DARK_YELLOW]],
[5, ' !!!!', STR_PAD_RIGHT, [Color::WHITE, COLOR::BOLD, Color::BG_MAGENTA]],
[10, '', STR_PAD_LEFT, [Color::CYAN]],
[6, '!!!', STR_PAD_BOTH, [Color::WHITE, COLOR::BOLD, Color::BG_MAGENTA]],
[60, ' ' . $this->exc2txt($exception, 80), STR_PAD_RIGHT, [Color::RED, Color::BG_BLACK]],
], 120);
} else {
echo Console::columns([
[9, $this->timeFormat($timeElapsed), STR_PAD_LEFT, [Color::DARK_YELLOW]],
[10, $this->tokensPerSecFormat($tokensPerSec), STR_PAD_LEFT, [Color::CYAN]],
[5, $isCorrect ? ' OK ' : ' FAIL', STR_PAD_RIGHT, $isCorrect ? [Color::BG_GREEN, Color::WHITE] : [Color::BG_RED, Color::WHITE]],
[6, $metric->toString(), STR_PAD_BOTH, $metric->toCliColor()],
[60, ' ' . $answerLine, STR_PAD_RIGHT, [Color::WHITE, Color::BG_BLACK]],
], 120);
}
Expand Down
11 changes: 11 additions & 0 deletions src/Extras/Evals/Contracts/CanEvaluate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Cognesy\Instructor\Extras\Evals\Contracts;

use Cognesy\Instructor\Extras\Evals\Data\EvalInput;
use Cognesy\Instructor\Extras\Evals\Data\EvalOutput;

interface CanEvaluate
{
public function evaluate(EvalInput $input) : EvalOutput;
}
2 changes: 1 addition & 1 deletion src/Extras/Evals/Contracts/CanExecuteExperiment.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

interface CanExecuteExperiment
{
public static function fromEvalInput(EvalInput $input): self;
public function withEvalInput(EvalInput $input): self;
public function execute(): void;
public function getLLMResponse(): LLMResponse;
public function getAnswer(): mixed;
Expand Down
19 changes: 19 additions & 0 deletions src/Extras/Evals/Contracts/CanMapValues.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Cognesy\Instructor\Extras\Evals\Contracts;

/**
* Interface for mapping a set of values to an object.
*
* @template T
*/
interface CanMapValues
{
/**
* Maps an associative array of values to an instance of T.
*
* @param array<string, mixed> $values
* @return T
*/
public static function map(array $values);
}
10 changes: 10 additions & 0 deletions src/Extras/Evals/Contracts/Metric.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Cognesy\Instructor\Extras\Evals\Contracts;

interface Metric
{
public function value() : mixed;
public function toLoss() : float;
public function toScore() : float;
}
Loading

0 comments on commit 066a205

Please sign in to comment.