Skip to content

Instantly share code, notes, and snippets.

@eduPHP
Created September 21, 2025 18:20
Show Gist options
  • Select an option

  • Save eduPHP/50c3c9e8a7fc31f83027bb3088c1cd0a to your computer and use it in GitHub Desktop.

Select an option

Save eduPHP/50c3c9e8a7fc31f83027bb3088c1cd0a to your computer and use it in GitHub Desktop.
Samples for Mixtape Co
<?php
namespace App\DTO;
use App\DTO\Contracts\AIInputOptions as AIInputOptionsInterface;
use App\Models\Optimization;
use Illuminate\Support\Collection;
class AIInputOptions implements AIInputOptionsInterface
{
public Collection $settings;
public function __construct(
public string $resume = "",
public bool $makeGrammaticalCorrections = false,
public bool $changeProfessionalSummary = false,
public bool $generateCoverLetter = false,
public bool $changeTargetRole = false,
public bool $mentionRelocationAvailability = false,
public string $roleName = '',
public string $roleDescription = '',
public ?string $roleLocation = '',
public string $roleCompany = '',
public array $metadata = [],
Collection $options = null,
) {
$this->settings = $options ?? collect();
}
public static function fromOptimization(Optimization $optimization): static
{
$content = $optimization->resume->detected_content;
return new static(
resume: $content,
makeGrammaticalCorrections: $optimization->make_grammatical_corrections,
changeProfessionalSummary: $optimization->change_professional_summary,
generateCoverLetter: $optimization->generate_cover_letter,
changeTargetRole: $optimization->change_target_role,
mentionRelocationAvailability: $optimization->mention_relocation_availability,
roleName: $optimization->role_name,
roleDescription: $optimization->role_description,
roleLocation: $optimization->role_location,
roleCompany: $optimization->role_company,
metadata: [
'optimization_id' => $optimization->id,
],
options: $optimization->user->ai_settings,
);
}
public function system(): array
{
$config = [
// Role + tone
'You are a job seeker matching your resume to job postings. Answer ONLY in JSON. Be direct: no softening.
If the resume has bad compatibility bad, say so. If there is no chance of getting the job, state it directly.',
// Resume context
"The following is your resume and single source of truth:\n\n{$this->resume}",
// Resume formatting rules
'Resume formatting rules:
- Contact info → <p class="contact-info">text | text | text</p>
- Section titles → <h2 class="section-title">Section Title</h2><div>Content</div>
- Name → <h1 class="name">Full Name</h1><h3 class="job-title">Job Title</h3>
- Experience → <h3 class="exp-title">Title</h3><p class="exp-period">place and period</p><ul>Description</ul>',
// Resume improvement instructions
$this->changeProfessionalSummary
? "Replace the Professional Summary with a role-specific one, emphasizing your strengths (keep the title)."
: false,
$this->changeTargetRole
? "Replace the Target Role (below the name) with: {$this->roleName}."
: false,
$this->mentionRelocationAvailability
? "At the bottom (class=\"footer\"), add:
'Available for remote work or relocation to {$this->roleLocation} through visa sponsorship'"
: false,
$this->mentionRelocationAvailability
? "If the company does not sponsor visas, include this fact in the 'findings' output list as a missing_requirement."
: false,
$this->makeGrammaticalCorrections
? "Correct grammar across the resume."
: false,
// Scoring logic
"When compatibility_score < {$this->settings['compatibilityScoreLevels']['medium']},
provide short-term and long-term improvement plans.",
// API enforcement
'Always reply ONLY with a valid JSON object that matches the schema.
Do not add explanations, prefaces, or comments outside JSON.',
$this->settings['instructions'] ?? false,
];
return array_filter($config);
}
public function user(): array
{
return [
"Optimize your resume for the {$this->roleLocation} market.
- Reorganize sections to match {$this->roleLocation}'s best practices.
- Improve content for higher selection rate.
- Keep the title intact.
- Role description is: {$this->roleDescription}
- Translate the output resume to its language!",
];
}
public function schema(): array
{
return [
"name" => "candidate_evaluation",
"schema" => [
"type" => "object",
"additionalProperties" => false,
"properties" => [
"resume" => [
"type" => "string",
"description" => "HTML resume, body content only, with basic styling",
],
"compatibility_score" => [
"type" => "integer",
"description" => "REALISTIC 0-100 score",
],
"top_choice" => [
"type" => "string",
"maxLength" => 400,
"description" => "If compatibility_score >= {$this->settings['compatibilityScoreLevels']['top']}, first-person pitch explaining why this is a top choice. Otherwise empty string.",
],
"professional_summary" => [
"type" => "string",
"description" => "Same professional summary included in the resume",
],
"language" => [
"type" => "string",
"description" => "Language of the role description i.e. 'en_US, pt_BR, etc.'",
],
"cover_letter" => [
"type" => "array",
"description" => $this->generateCoverLetter
? "Return 3 paragraphs with casual phrasing, no intro or signature."
: "Empty array.",
"items" => ["type" => "string"],
],
"findings" => [
"type" => "array",
"description" => "Consolidated evaluation items: strong alignments (at least 2), moderate gaps, missing requirements, or issues.",
"items" => [
"type" => "object",
"additionalProperties" => false,
"properties" => [
"group" => [
"type" => "string",
"enum" => ["strong_alignment", "moderate_gap", "missing_requirement", "issue"],
"description" => "Classification of the finding.",
],
"title" => ["type" => "string"],
"description" => ["type" => "string"],
],
"required" => ["group", "title", "description"],
],
],
"reasoning" => [
"type" => "string",
"description" => "Would you recommend this candidate? How can they improve their resume to match the job description?",
],
],
"required" => [
"resume",
"compatibility_score",
"professional_summary",
"language",
"cover_letter",
"findings",
"reasoning",
"top_choice",
],
],
];
}
public function metadata(): array
{
return $this->metadata;
}
public function getTextPrompt(): string
{
$prompt = "";
collect($this->system())->each(function ($line) use (&$prompt) {
$prompt .= "# System:\n$line\n\n";
});
collect($this->user())->each(function ($line) use (&$prompt) {
$prompt .= "# User:\n$line\n\n";
});
return $prompt;
}
public function toArray(): array
{
return [
'system' => $this->system(),
'user' => $this->user(),
'metadata' => $this->metadata(),
];
}
public function __toString(): string
{
return $this->getTextPrompt();
}
public function model(): string
{
return $this->settings['model'] ?? config('openai.model');
}
public function idempotencyKey(): string
{
return $this->model() . '-response-' . sha1($this->__toString());
}
}
<x-layout>
<x-slot:breadcrumbs>
<x-breadcrumbs.appointments.appointments-breadcrumbs :$for/>
</x-slot:breadcrumbs>
<x-card.wrapper stack>
<x-card>
<x-title>{{ __('Daily Calendar (By Zone & Assignees)') }}</x-title>
<x-card.row class="mx-0 mb-2">
<x-card.column class="md:w-1/2">
<livewire:calendars.partials.zone-filter :wire:key="'zone-filter-'.time()" :$for/>
</x-card.column>
<x-card.column class="md:w-1/2">
<livewire:calendars.partials.grid-results :wire:key="'result-filter-'.time()" />
</x-card.column>
</x-card.row>
<livewire:calendars.user-view :wire:key="'by-user-scheduler-'.time()" cy="calendars-user-view" :$for />
</x-card>
</x-card.wrapper>
</x-layout>
<?php
namespace Modules\OpenAI;
use App\DTO\Contracts\AIInputOptions;
use App\DTO\Contracts\AIAgentPrompter;
use App\DTO\Contracts\AIResponseDTO;
use App\Exceptions\RequestException;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Http;
class OpenAIPrompter implements AIAgentPrompter
{
public function handle(AIInputOptions $options): AIResponseDTO
{
$incrementUsage = ! cache()->has($options->idempotencyKey());
/** @var array $content */
$content = cache()->remember($options->idempotencyKey(), now()->addDay(), function () use ($options) {
$response = Http::withToken(config('openai.openai_api_key'))
->withOptions(['timeout' => 300])
->post(config('openai.endpoint'), [
'model' => $options->model(),
'store' => true,
'metadata' => $options->metadata(),
'input' => [
...array_map(fn($instruction) => ['role' => 'system', 'content' => $instruction], $options->system()),
...array_map(fn($instruction) => ['role' => 'user', 'content' => $instruction], $options->user()),
],
...$this->getSchemaSettings($options)
]);
if ($response->successful() === false) {
throw new RequestException($response->json('error.message'), $response->getStatusCode());
}
return $response->json();
});
\Log::debug('OpenAI Response', [$options->idempotencyKey() => $content]);
return new OpenAiResponse(
...$this->cleanup($content['output']),
usage: $incrementUsage ? $content['usage']['total_tokens'] : 0,
id: $content['id'],
model: $content['model'],
);
}
private function cleanup(array $outputs): array
{
$response = Arr::first($outputs, function ($output) {
return $output['type'] === 'message';
});
return json_decode($response['content'][0]['text'], true);
}
private function getSchemaSettings(AIInputOptions $options): array
{
$textFormat = ['gpt-4.1', 'gpt-4o-mini'];
if (in_array(config('openai.model'), $textFormat)) {
return [
'text' => [
'format' => [
'type' => 'json_schema',
...$options->schema(),
],
],
];
}
return [
'response_format' => [
'type' => 'json_schema',
'json_schema' => $options->schema(),
]
];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment