{"id":17122044,"url":"https://github.com/xp-forge/openai","last_synced_at":"2026-02-21T11:00:50.071Z","repository":{"id":257824965,"uuid":"871975264","full_name":"xp-forge/openai","owner":"xp-forge","description":"OpenAI APIs for XP Framework","archived":false,"fork":false,"pushed_at":"2026-01-17T11:14:25.000Z","size":181,"stargazers_count":0,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-17T22:03:03.686Z","etag":null,"topics":["azure-ai","azureai","embeddings","function-calling","load-balancing","openai","openai-api","openai-api-client","openai-realtime","openai-streaming","php7","php8","responses-api","rest-api","tiktoken","tiktoken-php","xp-framework"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xp-forge.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-10-13T13:18:56.000Z","updated_at":"2026-01-17T10:54:45.000Z","dependencies_parsed_at":"2024-10-23T01:44:44.147Z","dependency_job_id":"5df1f0ea-c2b4-40f2-ae86-af6feea49e54","html_url":"https://github.com/xp-forge/openai","commit_stats":null,"previous_names":["xp-forge/openai"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/xp-forge/openai","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Fopenai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Fopenai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Fopenai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Fopenai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xp-forge","download_url":"https://codeload.github.com/xp-forge/openai/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xp-forge%2Fopenai/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29679049,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T09:33:50.764Z","status":"ssl_error","status_checked_at":"2026-02-21T09:33:19.949Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure-ai","azureai","embeddings","function-calling","load-balancing","openai","openai-api","openai-api-client","openai-realtime","openai-streaming","php7","php8","responses-api","rest-api","tiktoken","tiktoken-php","xp-framework"],"created_at":"2024-10-14T18:06:18.144Z","updated_at":"2026-02-21T11:00:50.065Z","avatar_url":"https://github.com/xp-forge.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"OpenAI APIs for XP\n==================\n\n[![Build status on GitHub](https://github.com/xp-forge/openai/workflows/Tests/badge.svg)](https://github.com/xp-forge/openai/actions)\n[![XP Framework Module](https://raw.githubusercontent.com/xp-framework/web/master/static/xp-framework-badge.png)](https://github.com/xp-framework/core)\n[![BSD Licence](https://raw.githubusercontent.com/xp-framework/web/master/static/licence-bsd.png)](https://github.com/xp-framework/core/blob/master/LICENCE.md)\n[![Requires PHP 7.4+](https://raw.githubusercontent.com/xp-framework/web/master/static/php-7_4plus.svg)](http://php.net/)\n[![Supports PHP 8.0+](https://raw.githubusercontent.com/xp-framework/web/master/static/php-8_0plus.svg)](http://php.net/)\n[![Latest Stable Version](https://poser.pugx.org/xp-forge/openai/version.svg)](https://packagist.org/packages/xp-forge/openai)\n\nThis library implements OpenAI APIs with a low-level abstraction approach, supporting their REST and realtime APIs, request and response streaming, function calling and TikToken encoding.\n\nQuick start\n-----------\nUsing the REST API, see https://platform.openai.com/docs/api-reference/making-requests\n\n```php\nuse com\\openai\\rest\\OpenAIEndpoint;\nuse util\\cmd\\Console;\n\n$ai= new OpenAIEndpoint('https://'.getenv('OPENAI_API_KEY').'@api.openai.com/v1');\n\nConsole::writeLine($ai-\u003eapi('/responses')-\u003einvoke([\n  'model' =\u003e 'gpt-5.2',\n  'input' =\u003e $prompt,\n]));\n```\n\nStreaming\n---------\nThe REST API can use server-sent events to stream responses, see https://platform.openai.com/docs/api-reference/streaming\n\n```php\nuse com\\openai\\rest\\OpenAIEndpoint;\nuse util\\cmd\\Console;\n\n$ai= new OpenAIEndpoint('https://'.getenv('OPENAI_API_KEY').'@api.openai.com/v1');\n\n$events= $ai-\u003eapi('/responses')-\u003estream([\n  'model' =\u003e 'gpt-5.2',\n  'input' =\u003e $prompt,\n]);\nforeach ($events as $type =\u003e $value) {\n  Console::write('\u003c', $type, '\u003e ', $value);\n}\nConsole::writeLine();\n```\n\nTo access the result object, check for the *response.completed* event type and use its value. It contains the outuputs as well as model, filter results and usage information.\n\nTikToken\n--------\nEncodes text to tokens. Download the vocabularies [cl100k_base](https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken) (used for GPT-3.5 and GPT-4.0) and [o200k_base](https://openaipublic.blob.core.windows.net/encodings/o200k_base.tiktoken) (used for Omni and O1) first!\n\n```php\nuse com\\openai\\{Encoding, TikTokenFilesIn};\n\n$source= new TikTokenFilesIn('.');\n\n// By name =\u003e [9906, 4435, 0]\n$tokens= Encoding::named('cl100k_base')-\u003eload($source)-\u003eencode('Hello World!');\n\n// By model =\u003e [13225, 5922, 0]\n$tokens= Encoding::for('omni')-\u003eload($source)-\u003eencode('Hello World!');\n```\n\nInstead of *encode()*, you can use *count()* to count the number of tokens.\n\nEmbeddings\n----------\nTo create an embedding for a given text, use https://platform.openai.com/docs/guides/embeddings/what-are-embeddings\n\n```php\nuse com\\openai\\rest\\OpenAIEndpoint;\nuse util\\cmd\\Console;\n\n$ai= new OpenAIEndpoint('https://'.getenv('OPENAI_API_KEY').'@api.openai.com/v1');\n\nConsole::writeLine($ai-\u003eapi('/embeddings')-\u003einvoke([\n  'input' =\u003e $text,\n  'model' =\u003e 'text-embedding-3-small'],\n));\n```\n\nText to speech\n--------------\nTo stream generate audio, use the API's *transmit()* method, which sends the given payload and returns the response. See https://platform.openai.com/docs/guides/text-to-speech/overview\n\n```php\nuse com\\openai\\rest\\OpenAIEndpoint;\nuse util\\cmd\\Console;\n\n$ai= new OpenAIEndpoint('https://'.getenv('OPENAI_API_KEY').'@api.openai.com/v1');\n$payload= [\n  'input' =\u003e $input,\n  'voice' =\u003e 'alloy',  // or: echo, fable, onyx, nova, shimmer\n  'model' =\u003e 'tts-1',\n];\n\n$stream= $ai-\u003eapi('/audio/speech')-\u003etransmit($payload)-\u003estream();\nwhile ($stream-\u003eavailable()) {\n  Console::write($stream-\u003eread());\n}\n```\n\nSpeech to text\n--------------\nTo convert audio into text, upload files via the API's *open()* method, which returns an *Upload* instance. See https://platform.openai.com/docs/guides/speech-to-text/overview\n\n```php\nuse com\\openai\\rest\\OpenAIEndpoint;\nuse io\\File;\nuse util\\cmd\\Console;\n\n$ai= new OpenAIEndpoint('https://'.getenv('OPENAI_API_KEY').'@api.openai.com/v1');\n$file= new File($argv[1]);\n\n$response= $ai-\u003eapi('/audio/transcriptions')\n  -\u003eopen(['model' =\u003e 'whisper-1'])\n  -\u003etransfer('file', $file-\u003ein(), $file-\u003efilename)\n  -\u003efinish()\n;\nConsole::writeLine($response-\u003evalue());\n```\n\nYou can also stream uploads from *InputStream*s as follows:\n\n```php\n// ...setup code from above...\n\n$upload= $ai-\u003eapi('/audio/transcriptions')-\u003eopen(['model' =\u003e 'whisper-1']);\n\n$stream= $upload-\u003estream('file', 'audio.mp3');\nwhile ($in-\u003eavailable()) {\n  $stream-\u003ewrite($in-\u003eread());\n}\n$response= $upload-\u003efinish();\n\nConsole::writeLine($response-\u003evalue());\n```\n\nTracing the calls\n-----------------\nREST API calls can be traced with the [logging library](https://github.com/xp-framework/logging):\n\n```php\nuse com\\openai\\rest\\OpenAIEndpoint;\nuse util\\log\\Logging;\n\n$ai= new OpenAIEndpoint('https://'.getenv('OPENAI_API_KEY').'@api.openai.com/v1');\n$ai-\u003esetTrace(Logging::all()-\u003etoConsole());\n\n// ...perform API calls...\n```\n\nTool calls\n----------\nThere are two types of tools: Built-ins like *file_search* and *code_interpreter* (available [in the assistants API](https://platform.openai.com/docs/assistants/tools)) as well as custom functions, see https://platform.openai.com/docs/guides/function-calling \n\n### Defining functions\n\nCustom functions map to instance methods in a class:\n\n```php\nuse com\\openai\\tools\\Param;\nuse webservices\\rest\\Endpoint;\n\nclass Weather {\n  private $endpoint;\n\n  public function __construct(string $base= 'https://wttr.in/') {\n    $this-\u003eendpoint= new Endpoint($base);\n  }\n\n  public function in(#[Param] string $city): string {\n    return $this-\u003eendpoint-\u003eresource('/{0}?0mT', [$city])-\u003eget()-\u003econtent(); \n  }\n}\n```\n\nThe *Param* annnotation may define a description and a [JSON schema type](https://json-schema.org/understanding-json-schema/reference):\n\n* `#[Param('The name of the city')] $name`\n* `#[Param(type: ['type' =\u003e 'string', 'enum' =\u003e ['C', 'F']])] $unit`\n\n### Passing custom functions\n\nCustom functions are registered in a `Functions` instance and passed via *tools* inside the payload.\n\n```php\nuse com\\openai\\rest\\OpenAIEndpoint;\nuse com\\openai\\tools\\{Tools, Functions};\n\n$functions= (new Functions())-\u003eregister('weather', new Weather());\n\n$ai= new OpenAIEndpoint('https://'.getenv('OPENAI_API_KEY').'@api.openai.com/v1');\n$payload= [\n  'model' =\u003e 'gpt-5.2',\n  'tools' =\u003e new Tools($functions),\n  'input' =\u003e [['type' =\u003e 'message', 'role' =\u003e 'user', 'content' =\u003e $content]],\n];\n```\n\n### Invoking custom functions\n\nIf tool calls are requested by the LLM, invoke them and return to next completion cycle. See https://platform.openai.com/docs/guides/function-calling/configuring-parallel-function-calling\n\n```php\nuse util\\cmd\\Console;\n\n// ...setup code from above...\n\n$calls= $functions-\u003ecalls()-\u003ecatching(fn($t) =\u003e $t-\u003eprintStackTrace());\nnext: $result= $ai-\u003eapi('/responses')-\u003einvoke($payload));\n\n// If function calls are requested, invoke them and return to next response cycle\n$invokations= false;\nforeach ($result['output'] as $output) {\n  if ('function_call' !== $output['type']) continue;\n\n  $invokations= true;\n  $return= $calls-\u003ecall($call['name'], $call['arguments']);\n\n  $payload['input'][]= $call;\n  $payload['input'][]= [\n    'type'    =\u003e 'function_call_output',\n    'call_id' =\u003e $call['call_id'],\n    'output'  =\u003e $return,\n  ];\n}\nif ($invokations) goto next;\n\n// Print out final result\nConsole::writeLine($result);\n```\n\n### Passing context\n\nFunctions can be passed a context as follows by annotating parameters with the *Context* annotation:\n\n```php\nuse com\\mongodb\\{Collection, Document, ObjectId};\nuse com\\openai\\tools\\{Context, Param};\n\n// Declaration\nclass Memory {\n\n  public function __construct(private Collection $facts) { }\n\n  public function store(#[Context] Document $user, #[Param] string $fact): ObjectId {\n    return $this-\u003efacts-\u003einsert(new Document(['owner' =\u003e $user-\u003eid(), 'fact' =\u003e $fact]))-\u003eid();\n  }\n}\n\n// ...shortened for brevity...\n\n$context= ['user' =\u003e $user];\n$return= $calls-\u003ecall($call['name'], $call['arguments'], $context);\n```\n\nAzure OpenAI\n------------\nThese endpoints differ slightly in how they are invoked, which is handled by the *AzureAI* implementation. See https://learn.microsoft.com/en-us/azure/ai-services/openai/overview\n\n```php\nuse com\\openai\\rest\\AzureAIEndpoint;\nuse util\\cmd\\Console;\n\n// Using V1 API\n$ai= new AzureAIEndpoint('https://'.getenv('AZUREAI_API_KEY').'@example.openai.azure.com/openai/v1');\n\n// Using API version\n$ai= new AzureAIEndpoint(\n  'https://'.getenv('AZUREAI_API_KEY').'@example.openai.azure.com/openai/deployments/gpt-5.2',\n  '2025-04-01-preview'\n);\n\nConsole::writeLine($ai-\u003eapi('/responses')-\u003einvoke([\n  'model' =\u003e 'gpt-5.2',\n  'input' =\u003e $prompt,\n]));\n```\n\nDistributing requests\n---------------------\nThe *Distributed* endpoint allows to distribute requests over multiple endpoints. The *ByRemainingRequests* class uses the `x-ratelimit-remaining-requests` header to determine the target. See https://platform.openai.com/docs/guides/rate-limits\n\n```php\nuse com\\openai\\rest\\{AzureAIEndpoint, Distributed, ByRemainingRequests};\nuse util\\cmd\\Console;\n\n$endpoints= [\n  new AzureAIEndpoint('https://...@r1.openai.azure.com/openai/v1'),\n  new AzureAIEndpoint('https://...@r2.openai.azure.com/openai/v1'),\n];\n\n$ai= new Distributed($endpoints, new ByRemainingRequests());\n\nConsole::writeLine($ai-\u003eapi('/responses')-\u003einvoke([\n  'model' =\u003e 'gpt-5.2',\n  'input' =\u003e $prompt,\n]));\nforeach ($endpoints as $i =\u003e $endpoint) {\n  Console::writeLine('Endpoint #', $i, ': ', $endpoint-\u003erateLimit());\n}\n```\n\nFor more complex load balancing, have a look at [this blog article using Azure API management](https://techcommunity.microsoft.com/t5/apps-on-azure-blog/openai-at-scale-maximizing-api-management-through-effective/ba-p/4240317)\n\nRealtime API\n------------\nThe realtime API allows streaming audio and/or text to and from language models, see https://platform.openai.com/docs/guides/realtime\n\n```php\nuse com\\openai\\realtime\\RealtimeApi;\nuse util\\cmd\\Console;\n\n$api= new RealtimeApi('wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview');\n$session= $api-\u003econnect([\n  'Authorization' =\u003e 'Bearer '.getenv('OPENAI_API_KEY'),\n  'OpenAI-Beta'   =\u003e 'realtime=v1',\n];\nConsole::writeLine($session);\n\n// Send prompt\n$api-\u003etransmit([\n  'type' =\u003e 'conversation.item.create',\n  'item' =\u003e [\n    'type'    =\u003e 'message',\n    'role'    =\u003e 'user',\n    'content' =\u003e [['type' =\u003e 'input_text', 'text' =\u003e $message]],\n  ]\n]);\n\n// Receive response(s)\n$api-\u003esend(['type' =\u003e 'response.create', 'response' =\u003e ['modalities' =\u003e ['text']]]);\ndo {\n  $event= $api-\u003ereceive();\n  Console::writeLine($event);\n} while ('response.done' !== $event['type'] \u0026\u0026 'error' !== $event['type']);\n\n$api-\u003eclose();\n```\n\nFor Azure AI, the setup code is slightly different:\n\n```php\nuse com\\openai\\realtime\\RealtimeApi;\nuse util\\cmd\\Console;\n\n$api= new RealtimeApi('wss://example.openai.azure.com/openai/realtime', [\n  'api-version' =\u003e '2024-10-01-preview',\n  'deployment'  =\u003e 'gpt-4o-realtime-preview',\n]);\n$session= $api-\u003econnect(['api-key' =\u003e getenv('AZUREAI_API_KEY')]);\n```\n\nCompletions API\n---------------\nTo use the legacy (but industry standard) chat completions API, see https://platform.openai.com/docs/quickstart?api-mode=chat:\n\n```php\nuse com\\openai\\rest\\OpenAIEndpoint;\nuse util\\cmd\\Console;\n\n$ai= new OpenAIEndpoint('https://'.getenv('OPENAI_API_KEY').'@api.openai.com/v1');\n\n$flow= $ai-\u003eapi('/chat/completions')-\u003eflow([\n  'model'    =\u003e 'gpt-5.2',\n  'messages' =\u003e [['role' =\u003e 'user', 'content' =\u003e $prompt]],\n]);\nforeach ($flow-\u003edeltas() as $type =\u003e $delta) {\n  Console::writeLine('\u003c', $type, '\u003e ', $delta);\n}\nConsole::writeLine();\n```\n\nThe result object is computed from the streamed deltas and can be retrieved by accessing *$flow-\u003eresult()*.\n\nSee also\n--------\n* https://github.com/openai/tiktoken/\n* https://github.com/openai/openai-python\n* https://github.com/openai/openai-node\n* https://github.com/Azure-Samples/azure-openai-reverse-proxy\n* https://www.youtube.com/watch?v=i-oHvHejdsc - GPT Function calling in a nutshell","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxp-forge%2Fopenai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxp-forge%2Fopenai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxp-forge%2Fopenai/lists"}