aquidify / sdk
PHP client for the Aquidify API, with a Laravel adapter: turn what people type into validated, structured intent.
Requires
- php: >=8.2
- ext-curl: *
- ext-json: *
Requires (Dev)
- orchestra/testbench: ^10.0|^11.0
- phpunit/phpunit: ^11.0|^12.0
Suggests
- illuminate/support: Laravel adapter: config, auto-discovered service provider, Aquidify facade and Aquidify::fake() (^11.0|^12.0|^13.0)
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-09-22 17:50:29 UTC
README
Aquidify SDKs
Official clients for the Aquidify API: turn what people type into validated, structured intent your software can act on.
"Iščem delo v skladišču v Ljubljani, brez nočnih."
→ roles: warehouse · locations: Ljubljana · schedules: night (exclude)
Every extracted item quotes the exact span it came from (raw_text),
uncertainty is kept instead of guessed away, and "unknown" is never confused
with "any". Aquidify interprets; your application decides.
| Language | Folder | Install today (from GitHub) | Registry |
|---|---|---|---|
| PHP 8.2+ / Laravel | php/ |
see PHP below | Packagist aquidify/sdk, coming soon |
| TypeScript / JavaScript | js/ |
build from source: cd js && npm install && npm run build |
npm @aquidify/sdk, coming soon |
| Go | go/ |
go get github.com/aquidify/sdk/go@v0.1.0 |
✓ published |
| Python 3.9+ | python/ |
pip install "git+https://github.com/aquidify/sdk@v0.1.0#subdirectory=python" |
PyPI aquidify, coming soon |
| Java 17+ | java/ |
cd java && mvn install, then depend on com.aquidify:aquidify-sdk:0.1.0 |
Maven Central, coming soon |
| Rust | rust/ |
aquidify = { git = "https://github.com/aquidify/sdk", tag = "v0.1.0" } |
crates.io aquidify, coming soon |
| curl / any language | curl/ |
nothing to install | — |
Not listed? Generate a client from openapi.yaml, the source
of truth for every SDK here. Output shapes per domain are JSON Schemas in
schemas/.
All clients read AQUIDIFY_API_KEY from the environment when no key is passed.
Keep keys server-side: never ship one to a browser or mobile app.
PHP
Until the package is on Packagist, add the repository to your composer.json:
composer config repositories.aquidify vcs https://github.com/aquidify/sdk composer require aquidify/sdk:^0.3
$aq = new Aquidify\Client(); // AQUIDIFY_API_KEY $r = $aq->interpret('hiring.candidate', 'Iščem delo v skladišču v Ljubljani, brez nočnih.', 'sl-SI'); $r['interpretation']['intents'][0]['roles'][0]['value']; // "warehouse" $r['clarification']; // null
Your own task, for any industry (see curl/README.md):
$aq->putTask('support.ticket@1.0.0', [ 'instructions' => 'Classify customer support emails for routing.', 'schema' => ['type' => 'object', 'properties' => [ 'category' => ['enum' => ['billing', 'bug', 'refund', 'other']], 'order_id' => ['type' => 'string', 'description' => 'Order number, digits only'], ]], ]); $fields = $aq->interpret('support.ticket@1.0.0', $emailBody, 'en')['interpretation']['fields'];
Every SDK can interpret with a task id as the domain. Registering tasks
(putTask / getTask / listTasks) is in the PHP, TypeScript, Go and Python
SDKs (put_task … in Python); from Java and Rust use plain HTTP for now.
Laravel
The PHP package includes a Laravel adapter (auto-discovered): config, a
service provider, the Aquidify facade and a fake for tests.
# .env AQUIDIFY_API_KEY=... AQUIDIFY_TIMEOUT=8 # seconds; keep short in web requests and fall back AQUIDIFY_PARSER_VERSION=1.0.0 # pinned by default AQUIDIFY_SCHEMA_VERSION=1.0.0 php artisan vendor:publish --tag=aquidify-config # optional
use Aquidify\Interpreter; use Aquidify\Laravel\Facades\Aquidify; // inject… public function __construct(private Interpreter $aquidify) {} $r = $this->aquidify->interpret('hiring.candidate', $text, app()->getLocale()); // …or use the facade $r = Aquidify::interpret('hiring.candidate', $text, 'sl');
Tests never touch the network:
use Aquidify\Testing\FakeClient; Aquidify::fake([ 'skladišče' => FakeClient::interpretation(['intents' => [/* … */]]), // input substring => response '*' => FakeClient::error('model_unavailable'), // everything else fails ]); // … exercise your code … Aquidify::assertInterpreted(fn ($call) => $call['locale'] === 'sl');
Aquidify::fake() replaces both the facade and every injected Interpreter.
Without Laravel, use new FakeClient([...]) directly.
TypeScript / JavaScript
import { Aquidify, type HiringCandidateInterpretation } from "@aquidify/sdk"; const aq = new Aquidify(); const r = await aq.interpret<HiringCandidateInterpretation>({ domain: "hiring.candidate", input: "Iščem delo v skladišču v Ljubljani, brez nočnih.", locale: "sl-SI", }); r.interpretation.intents[0].schedules[0]; // { value: "night", polarity: "exclude", … }
Go
c, _ := aquidify.New("") r, err := c.Interpret(ctx, aquidify.Request{Domain: "hiring.candidate", Input: "…", Locale: "sl-SI"}) // r.Interpretation is json.RawMessage: unmarshal into your own types
Python
from aquidify import Aquidify r = Aquidify().interpret("hiring.candidate", "Iščem delo v skladišču v Ljubljani, brez nočnih.", "sl-SI") r["interpretation"]["intents"][0]["locations"][0]["value"] # "Ljubljana"
Java
var r = new Aquidify(null).interpret("hiring.candidate", "Iščem delo v skladišču v Ljubljani, brez nočnih.", "sl-SI"); r.interpretation().at("/intents/0/roles/0/value").asText(); // "warehouse"
Rust
let r = aquidify::Client::from_env()? .interpret(&aquidify::Request::new("hiring.candidate", "Iščem delo …", "sl-SI"))?; r.interpretation["intents"][0]["roles"][0]["value"]; // "warehouse"
Search engines: Meilisearch, Algolia, Elasticsearch
Aquidify reads the query; your search engine finds and ranks the documents. Filters (PHP) and
SearchFilters (TypeScript) turn an answer into that engine's filter. You map interpretation fields
to your index attributes; unmapped fields are ignored.
use Aquidify\Search\Filters; $r = $aq->interpret('hiring.candidate', 'Warehouse job in Maribor, mornings only, no weekends', 'en'); $f = Filters::from($r, ['roles' => 'category', 'locations' => 'city', 'schedules' => 'shift', 'salary' => 'salary']); $meili->index('jobs')->search('', ['filter' => $f->meilisearch()]); // category = "warehouse" AND city = "Maribor" AND shift = "morning" AND shift NOT IN ["weekend"] $algolia->searchSingleIndex('jobs', ['filters' => $f->algolia(), 'optionalFilters' => $f->algoliaOptional()]); $es->search(['index' => 'jobs', 'body' => ['query' => $f->elasticsearch()]]); // OpenSearch: same DSL
import { SearchFilters } from "@aquidify/sdk"; const f = SearchFilters.from(r, { roles: "category", locations: { attribute: "city", value: (v) => v.toLowerCase() } }); await index.search("", { filter: f.meilisearch() });
| In the answer | Becomes |
|---|---|
include + required |
a filter; several values for one attribute are OR-ed |
acceptable / conditional |
widens that filter, never filters on its own |
preferred |
a boost only: Algolia optionalFilters, Elasticsearch should (Meilisearch: none) |
exclude + required |
NOT |
{min, max} (salary, price) |
a numeric range |
| "any", or not said | nothing: no filter |
One sentence can describe several searches (hiring intents). Meilisearch and Elasticsearch get them
OR-ed; Algolia cannot OR groups, so run searches() as a multi-query. For Elasticsearch, map text
fields to their keyword sub-field (city.keyword). Translate values into your index's vocabulary with
['attribute' => 'city', 'value' => fn ($v) => ...].
Errors
Every client raises one error type carrying the HTTP status, the API's
stable code (unauthorized, rate_limited, invalid_request,
model_unavailable, …, or network), the request_id and retry_after, plus
a retryable check. The full table is in curl/. Clients do not
retry on their own: retry retryable errors after retry_after, ideally with
an Idempotency-Key.
Versions
Responses carry parser_version and schema_version. Pin both in production
to keep the shape stable while newer parsers roll out. A released schema version
never changes; changes ship as new versions.
Testing the SDKs
Each SDK has a live smoke test that makes no model calls (it costs nothing):
export AQUIDIFY_API_KEY=... composer install && composer test # PHP + Laravel adapter cd js && npm install && npm test # TypeScript cd go && go test ./... # Go cd python && uv run python tests/smoke.py # Python cd java && mvn -q test # Java cd rust && cargo test # Rust
License
MIT