Overview
[!NOTE]
HTTP Proxy provides unified API to different chat completion and embedding models and applications.
Written in Java 21 and built on top of Eclipse Vert.x.
Read more about the DIAL Core
OpenAPI Specification
DIAL Core exposes an OpenAPI specification describing all available REST endpoints, request and response models, and common error responses.
The specification is generated automatically from the source code using the openapi-generator module. Do not edit the generated OpenAPI files manually, as any changes will be overwritten during the next generation.
The latest OpenAPI 3.0 specification is available in docs/open_api_core.yaml:
https://github.com/epam/ai-dial-core/blob/development/docs/open_api_core.yaml
Preview endpoints
Endpoints that are still under development are marked with the vendor extension:
x-preview: true
These endpoints are considered experimental and may change or be removed in future releases without backward compatibility guarantees.
Build 🏗
DIAL Core has a dependency on GitHub packages of JClouds.
Github doesn't provide anonymous access to packages.
That requires to pass credentials GitHub for access to published JClouds packages. See the code snippet below:
repositories {
maven {
url = uri("https://maven.pkg.github.com/epam/jclouds")
credentials {
username = project.findProperty("gpr.user") ?: System.getenv("GPR_USERNAME")
password = project.findProperty("gpr.key") ?: System.getenv("GPR_PASSWORD")
}
}
mavenCentral()
}
[!IMPORTANT]
You should set env variables GPR_USERNAME and GPR_PASSWORD to valid values, where GPR_USERNAME - GitHub username and GPR_PASSWORD - GitHub personal access token.
[!IMPORTANT]
The access token requires the permission read:packages.
See more details here to generate personal access token in GitHub.
Build the project with Gradle and Java 21:
./gradlew build
Run ▶️
Run the project with Gradle:
./gradlew :server:run
Or run com.epam.aidial.core.server.AiDial class from your favorite IDE.
Helm Deployment
You have the option to deploy the DIAL Core on the Kubernetes cluster by utilizing an umbrella dial Helm chart, which also deploys other DIAL components. Alternatively, you can use dial-core Helm chart to deploy just Core.
[!NOTE]
Refer to Examples for guidelines.
In any case, in your Helm values file, it is necessary to provide application's configurations described in the Configuration section.
Configuration ⚙️
Static settings
[!NOTE]
Static settings are used on startup and cannot be changed while application is running. Refer to example to view the example configuration file.
Priority order:
- Environment variables with extra "aidial." prefix. E.g. "aidial.server.port", "aidial.config.files".
- File specified in "AIDIAL_SETTINGS" environment variable.
- Default resource file: src/main/resources/aidial.settings.json.
| Setting | Default | Required | Description |
|---|
| config.files | aidial.config.json | No | List of paths to dynamic settings. Refer to example of the file with dynamic settings. |
| config.reload | 60000 | No | Config reload interval in milliseconds. |
| vertx.* | - | No | Vertx settings. Refer to vertx.io to learn more. |
| server.* | - | No | Vertx HTTP server settings for incoming requests. Refer to HTTP server options to learn more. |
| server.instances | 1 | No | Number of HTTP servers Core starts on server.port. Vert.x hands each new client connection to the next one, so connections spread over that many event loops; the event-loop count (vertx.eventLoopPoolSize) is the useful maximum. A value above 1 lets a pod serve more requests per second; where analytics rows carry request and response bodies (API keys without secured), the analytics writer can then fall behind and fill the heap, so set analytics.maxPendingBodyHeapPercent there, or keep 1. With a value above 1, server.port must not be 0 (use a fixed port, or -1 for one shared random port). Env: aidial.server.instances. |
| client.* | - | No | Vertx HTTP client settings for outbound requests. Refer to HTTP client options to learn more. |
|
Collecting claims
analytics.collectClaims and analytics.claimsAllowlist are independent: either one alone is enough to emit the
claims object, and each contributes only its own members. While collectClaims is on, an allowlist entry named
user_id, roles or user_display_name is skipped whenever that member was already written from the fixed set.
Because each allowlist entry is emitted under the exact path it was configured with, one list can cover several
providers that name the same thing differently — e.g. ["email", "upn", "preferred_username"]. Paths that are not
dot-separated claim names (".", "a..b") are rejected at startup with a warning. A claim value larger than 4 KB is
emitted as a truncated string ending in >>, the same marker used for oversized headers and bodies.
Prefer the hashed user id where you can: when identityProviders.*.loggingKey points at the email claim and
identityProviders.*.obfuscateUserEmail is left enabled, user.id already identifies the same user as a salted hash,
without putting the raw address in the log.
| Setting | Default | Required | Description |
|---|
| identityProviders | - | Yes | Map of identity providers. Note: At least one identity provider must be provided. Refer to examples to view available providers. Refer to IDP Configuration to view guidelines for configuring supported providers. |
| identityProviders | - | Yes | Map of identity providers. Note: At least one identity provider must be provided. Refer to examples to view available providers. Refer to IDP Configuration to view guidelines for configuring supported providers. |
| identityProviders.*.jwksUrl | - | Optional | Url to jwks provider. Required if disabledVerifyJwt is set to false. Note: Either jwksUrl or userInfoEndpoint must be provided. |
| identityProviders.*.userInfoEndpoint | - | Optional | Url to user info endpoint. Note: Either jwksUrl or userInfoEndpoint must be provided or disableJwtVerification is unset. Refer to Google example. |
| identityProviders.*.rolePath | - | Yes | Path(s) to the claim user roles in JWT token or user info response, e.g. resource_access.chatbot-ui.roles or just . Can be single String or Array of Strings. Refer to to view guidelines for configuring supported providers. |
| Setting | Default | Required | Description |
|---|
| toolsets.security.allowedRedirectUris | [] | Conditional | List of redirect URIs clients can provide during OAuth sign-in. A URI is accepted only if listed here or equal to the toolset's own redirect_uri; for dynamic client registration all URIs from this list are registered with the authorization server. Required when more than one client can start a sign-in (e.g. Chat and Admin panel), since a toolset can pin only one redirect_uri of its own. |
| toolsets.security.authorizationServers | - | No | Path(s) to the authorization server URLs trusted to issue access tokens for MCP clients. |
| toolsets.security.resourceSchema | https | No | Schema of the resource server. This URL schema is used to construct the resource identifier for token validation, as defined in RFC 9728. If not specified, the default value will be applied. |
| toolsets.security.resourceHost | - | No | The public, fully-qualified hostname of this resource server (e.g., api.example.com). This is used to construct the resource identifier for token validation per RFC 9728. If not set, the host is derived from the incoming request. |
| toolsets.security.scopesSupported | - | No | List of scope values, as defined in OAuth 2.0 [RFC6749], that are used in authorization requests to request access to this protected resource. |
| toolsets.security.kms.provider | unencrypted | No | Specifies KMS provider. Supported providers: aws, azure, gcp, unencrypted |
| toolsets.security.kms.keyId | - | No | Identifies the KMS key to use in the encryption operation. |
| toolsets.security.kms.region | - | No | Geo region where the KMS is located. Required if provider is set to aws. |
| toolsets.security.kms.encryptionAlgorithm | - | No | Encryption algorithm. if is set to . Refer to , to get the list of supported algorithms for azure. Default value for is |
| Setting | Default | Required | Description |
|---|
| storage.provider | filesystem | Yes | Specifies blob storage provider. Supported providers: s3, aws-s3, azureblob, google-cloud-storage, filesystem. See examples in the sections below. |
| storage.endpoint | - | Optional | Specifies endpoint url for s3 compatible storages. Note: The setting might be required. That depends on a concrete provider. |
| storage.identity | - | Optional | Blob storage access key. Can be optional for filesystem, aws-s3, google-cloud-storage providers. Refer to sections in this document dedicated to specific storage providers. |
| storage.credential | - | Optional | Blob storage secret key. Can be optional for filesystem, aws-s3, google-cloud-storage providers. |
| storage.bucket | - | No | Blob storage bucket. |
| storage.overrides.* | - | No | Key-value pairs to override storage settings. * might be any specific blob storage setting to be overridden. Refer to examples in the sections below. |
| storage.createBucket | false | No | Indicates whether bucket should be created on start-up. |
| storage.prefix | - | No | Base prefix for all stored resources. The purpose to use the same bucket for different environments, e.g. dev, prod, pre-prod. Must not contain path separators or any invalid chars. |
| storage.maxUploadedFileSize | 536870912 | No | Maximum size in bytes of uploaded file. If a size of uploaded file exceeds the limit the server returns HTTP code 413 |
| Setting | Default | Required | Description |
|---|
| encryption.secret | - | No | Secret is used for AES encryption of a prefix to the bucket blob storage. The value should be random generated string. |
| encryption.key | - | No | Key is used for AES encryption of a prefix to the bucket blob storage. The value should be random generated string. |
| Setting | Default | Required | Description |
|---|
| resources.maxSize | 67108864 | No | Max allowed size in bytes for a resource. |
| resources.maxSizeToCache | 1048576 | No | Max size in bytes for a resource to cache in Redis. |
| resources.syncPeriod | 60000 | No | Period in milliseconds, how frequently check for resources to sync. |
| resources.syncDelay | 120000 | No | Delay in milliseconds for a resource to be written back in object storage after last modification. |
| resources.syncBatch | 4096 | No | How many resources to sync in one go. |
| resources.cacheExpiration | 300000 | No | Expiration in milliseconds for synced resources in Redis. |
| resources.compressionMinSize | 256 | No | Compress a resource with gzip if its size in bytes more or equal to this value. |
| resources.resourceTypesExpiration | | No | Define expiration time per resource type in milliseconds |
| resources.resourceTypesExpiration.FILE | | 5 mins | Define expiration time for files |
| resources.resourceTypesExpiration.CONVERSATION | | 5 mins | Define expiration time for converations |
| resources.resourceTypesExpiration.PROMPT | | 5 mins | Define expiration time for prompts |
| resources.resourceTypesExpiration.APPLICATION | | 30 days | Define expiration time for applications |
| resources.resourceTypesExpiration.TOOL_SET | | 30 days | Define expiration time for toolsets |
| resources.resourceTypesExpiration.RULES | |
Per-resource limits for folder-as-resource complex resources (skills, projects), enforced on whole-resource
PUT and single-file PUT. Exceeding a limit rejects the request and leaves nothing observable written.
These limits also bound listing reads, archive (ZIP download) size and how long a mutation holds the
resource lock (a single-file PUT copies the whole current version under the lock).
| Setting | Default | Required | Description |
|---|
| complexResource.maxFiles | 100 | No | Max number of files a complex resource may contain. |
| complexResource.maxTotalBytes | 16777216 | No | Max aggregate size in bytes of all files in a complex resource. |
| complexResource.maxFileSizeBytes | 1048576 | No | Max size in bytes of a single file within a complex resource. |
| complexResource.types.<group> | - | No | Per-type override keyed by URL group (skills, projects) with any subset of maxFiles, maxTotalBytes, maxFileSizeBytes; unset fields fall back to the values above. The built-in projects override is 1000 files, 67108864 total bytes, 5242880 bytes per file; setting types replaces it. |
Periodic job that reclaims storage for folder-as-resource complex resources (e.g. skills): it finishes
reclaiming resources tombstoned by DELETE and GCs stale versions left behind by a failed/interrupted write.
| Setting | Default | Required | Description |
|---|
| complexResourceSweep.period | 10000 | No | Period in milliseconds between sweep ticks. |
| complexResourceSweep.batch | 1000 | No | Number of references listed (and potentially processed) per batch. |
| complexResourceSweep.activeBatches | 5 | No | Max number of batches this instance may have mid-processing in parallel at any moment. Enforced locally per instance; not a cluster-wide total. |
| complexResourceSweep.gracePeriod | 3600000 | No | Minimum age in milliseconds a tombstoned resource or a superseded version must reach before the sweep physically deletes it, to protect any reader that started before the delete/supersession. |
Periodic job that deletes the spend counters of withdrawn budgets: a row is removed once both its windows have closed
and no assignment or self-applying policy exists for its budget.
| Setting | Default | Required | Description |
|---|
| budgetCounterSweep.period | 300000 | No | Period in milliseconds between sweeps of the spend counters of withdrawn budgets. A row is deleted once both its windows have closed and no assignment or self-applying policy exists for its budget. |
| budgetCounterSweep.batch | 500 | No | Number of counter rows handled per sweep tick. |
Background responses are polled through this Core instance's own HTTP endpoint, so polling requests pass
through the interceptor chain.
| Setting | Default | Required | Description |
|---|
| backgroundJob.localBaseUrl | http://localhost:{actual server port} | No | Base URL that background job polling uses to reach this Core instance's own HTTP endpoint. |
| backgroundJob.pollTimeoutMs | 300000 | No | Timeout in milliseconds of a single polling request. A request exceeding it fails and counts as a poll failure. |
| backgroundJob.pollGiveUpStatusCodes | [401, 403, 404] | No | HTTP status codes of a polling response that make the job give up immediately instead of retrying. |
| backgroundJob.jobTtlMs | 86400000 | No | Lifetime in milliseconds of a background job and its per-request API key. The scheduler's next scan expires a job older than this. |
| backgroundJob.scanIntervalMs | 600000 | No | Interval in milliseconds between the scheduler's scans of stored jobs. A scan schedules each job for polling or expires it. |
Server-Sent Events channel used to relay JSON-RPC requests/responses between a toolset deployment and
its subscribed client (e.g. the toolset sign-in pop-up flow). A background watchdog periodically
verifies the underlying Redis pub/sub subscription is still alive and forces a resubscribe if Redis
silently dropped it (e.g. an idle connection killed by a NAT/load balancer), which otherwise would leave
the client subscribed with no events ever reaching it.
| Setting | Default | Required | Description |
|---|
| clientChannel.watchdog.period | 10000 | No | Period in milliseconds between watchdog checks of the Redis pub/sub subscription used to relay JSON-RPC requests/responses for the client channel feature. |
Settings of POST /v1/admin/system-export, which streams the public and platform buckets as a ZIP archive.
Refer to Admin system export to learn more.
| Setting | Default | Required | Description |
|---|
| adminSystemExport.stallTimeout | 60000 | No | Time in milliseconds the export waits for a slow client to take the buffered data before it fails and the connection is reset. Raise it if clients with slow connections fail. It must be positive and below the 5 minute lock period (300000), otherwise Core does not start. |
| Setting | Default | Required | Description |
|---|
| redis.singleServerConfig.address | - | Yes | Redis single server addresses, e.g. "redis://host:port". Either singleServerConfig or clusterServersConfig must be provided. |
| redis.clusterServersConfig.nodeAddresses | - | Yes | Json array with Redis cluster server addresses, e.g. ["redis://host1:port1","redis://host2:port2"]. Either singleServerConfig or clusterServersConfig must be provided. |
| redis.provider.* | - | No | Provider specific settings |
| redis.provider.name | - | Yes | Provider name. The valid values are aws-elasti-cache(see instructions), gcp-memory-store(see instructions), azure-redis-cache(see instructions. |
| redis.provider.userId | - | Yes | IAM-enabled user ID. Note. It's applied to aws-elasti-cache |
| redis.provider.accountName | - | Yes | The resource name of the service account for which the credentials are requested, in the following format: projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}. The - wildcard character is required; replacing it with a project ID is invalid. Note. It's applied to gcp-memory-store |
| redis.provider.region | - | Yes | Geo region where the cache is located. Note. It's applied to aws-elasti-cache |
|
| Setting | Default | Required | Description |
|---|
| access.admin.rules | - | No | Matches claims from identity providers with the rules to figure out whether a user is allowed to perform admin actions (READ and WRITE access to any resource, approving publication requests from DIAL users. Configuration example for DIAL Core: "access": {"admin": {"rules": [{"function": "EQUAL","source": "roles","targets": ["admin"]}]}} Where, function - a matching function one of TRUE (any user is admin), FALSE (noone is admin), EQUAL, CONTAIN, REGEX source - the path to the claim in the JWT token payload that should be evaluated against the targets. targets - is an array of values that the system checks for in the source claim. |
| access.globalReader.rules | - | No | Matches claims from identity providers with the rules to figure out whether a user is allowed to perform global reader actions (READ access to all public and private resources). Intended for background or scheduled jobs that need to read user data without write privileges. Applications (per-request API keys) are excluded even if they carry a matching role. Configuration example: "access": {"globalReader": {"rules": [{"function": "EQUAL","source": "roles","targets": ["global-reader"]}]}} |
| access.createCodeAppRoles | - | No | The list of user roles to be allowed to create custom code applications or run code interpreter. Note. Calls by per request key are permitted even if the originator doesn't have permissions. |
| Setting | Default | Required | Description |
|---|
| applications.includeCustomApps | false | No | The flag indicates whether applications should be included into openai listing (required for Code Apps, Custom Apps, Quick Apps, etc) |
| applications.controllerEndpoint | - | No | The endpoint to Application Controller Web Service that manages deployments for applications with functions |
| applications.controllerTimeout | 240000 | No | The timeout of operations to Application Controller Web Service |
| Setting | Default | Required | Description |
|---|
| codeInterpreter.sessionImage | - | No | The code interpreter session image to use |
| codeInterpreter.sessionProxyUrl | - | No | The code interpreter will be deployed as a pod instead of knative deployment and all requests will be proxied through nginx proxy |
| codeInterpreter.sessionTtl | 600000 | No | The session time to leave after the last API call |
| codeInterpreter.checkPeriod | 10000 | No | The interval at which to check active sessions for expiration |
| codeInterpreter.checkSize | 256 | No | The maximum number of active sessions to check in single check |
Storage requirements
DIAL Core stores user data in the following storages:
- Blob Storage keeps permanent data.
- Redis keeps volatile in-memory data for fast access.
[!NOTE]
Refer to Storage Requirements to learn more.
Dynamic settings
[!NOTE]
Dynamic settings are stored in JSON files, specified via "config.files" static setting, and reloaded at interval,
specified via "config.reload" static setting. Refer to example.
Dynamic settings can include the following parameters:
| Parameter | Description |
|---|
| routes | A list of registered routes in DIAL Core. Refer to Routes to see dynamic settings. |
| interceptors | A list of deployed DIAL Interceptors and their parameters. Refer to Interceptors to see dynamic settings. |
| globalInterceptors | A list of interceptors to be executed for any deployment on chat completion request. Refer to Interceptors to learn more. |
| applications | A list of deployed applications and their parameters. Refer to Applications to see dynamic settings. |
| models | A list of deployed models and their parameters. Refer to Models to see dynamic settings. |
| toolsets | A list of available toolsets and their parameters. Refer to Toolsets to see dynamic settings. |
| roles | API key or JWT roles and their parameters. Refer to Roles to see dynamic settings. |
| keys | API keys and their parameters. Refer to API Keys to see dynamic settings. |
| retriableErrorCodes | List of Retriable Error Codes for handling outages at LLM Providers. This list extends the existing error codes (429, 502, 503, 504) but doesn't override them. |
Claude Code commands
This repository ships custom Claude Code slash commands under .claude/commands/. Each command file documents its own usage and prerequisites.
The workflow is split across three commands so each invocation maps to a single phase. Use them in order: /start-issue → implement → /finish-issue → /open-pr.
| Command | Description | Prerequisites |
|---|
/start-issue <issue-number> | Fetch a GitHub issue and create a branch from latest development. | GitHub CLI (gh) installed and authenticated (gh auth login). |
/finish-issue | Run pre-commit checks (checkstyleMain, checkstyleTest, relevant tests) and commit using conventional commit format. | |
/open-pr | Push the current branch and open a PR against development. | GitHub CLI (gh) installed and authenticated (gh auth login). |
[!NOTE]
To add a new command, drop a Markdown file into .claude/commands/ and append a row to the table above.
Spec-driven process (OpenSpec)
Behaviour specs and change proposals live under openspec/ and follow OpenSpec:
openspec/specs/<capability>/spec.md describes current behaviour, openspec/changes/<id>/ holds a proposal with its spec deltas, design and tasks, and openspec/config.yaml holds the project rules. See the "Process" section of CLAUDE.md for the propose / apply / archive steps.
Prerequisites: Node.js 20.19 or newer. The OpenSpec CLI is pinned and run through npx, so no global install is required:
npx -y @fission-ai/[email protected] validate --all --strict
Claude Code entry points: the /opsx:* commands under .claude/commands/opsx/ (explore, propose, apply, update, sync, archive) and the sdd-* workflows under .claude/workflows/.
License
Copyright (C) 2024 EPAM Systems
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.