Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions models/telnyx/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
INSTALL_METHOD=remote
REMOTE_INSTALL_HOST=localhost
REMOTE_INSTALL_PORT=5003
REMOTE_INSTALL_KEY=
TELNYX_API_KEY=
14 changes: 14 additions & 0 deletions models/telnyx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Telnyx Model Provider

This plugin adds Telnyx AI model support for Dify.

Supported model types:

- LLM chat completions via `POST /v2/ai/chat/completions`
- Text embeddings via `POST /v2/ai/openai/embeddings`
- Text-to-speech via `POST /v2/text-to-speech/speech`
- Speech-to-text via `POST /v2/ai/audio/transcriptions`

Configure the provider with a Telnyx API key. The default API base is `https://api.telnyx.com`; override `telnyx_api_base` only when using a compatible proxy.

The predefined model YAMLs are aligned with Telnyx's official docs and the live `/v2/ai/openai/models` and `/v2/ai/openai/embeddings/models` model-list endpoints. Telnyx REST TTS docs and examples have some path ambiguity; this provider keeps using `POST /v2/text-to-speech/speech`, which is the endpoint verified by the live provider smoke tests.
6 changes: 6 additions & 0 deletions models/telnyx/_assets/icon_l_en.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions models/telnyx/_assets/icon_s_en.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions models/telnyx/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from dify_plugin import DifyPluginEnv, Plugin

plugin = Plugin(DifyPluginEnv())

if __name__ == "__main__":
plugin.run()
28 changes: 28 additions & 0 deletions models/telnyx/manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
version: 0.0.1
type: plugin
author: "langgenius"
name: "telnyx"
description:
en_US: Models provided by Telnyx AI, including chat, embeddings, speech-to-text, and text-to-speech.
label:
en_US: "Telnyx"
created_at: "2026-05-08T00:00:00Z"
icon: icon_s_en.svg
resource:
memory: 1048576
permission:
model:
enabled: true
llm: true
plugins:
models:
- "provider/telnyx.yaml"
meta:
version: 0.0.1
arch:
- "amd64"
- "arm64"
runner:
language: "python"
version: "3.12"
entrypoint: "main"
123 changes: 123 additions & 0 deletions models/telnyx/models/common_telnyx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
from __future__ import annotations

import json
from collections.abc import Mapping
from typing import Any
from urllib.parse import urljoin

import requests
from dify_plugin.errors.model import (
InvokeAuthorizationError,
InvokeBadRequestError,
InvokeConnectionError,
InvokeError,
InvokeRateLimitError,
InvokeServerUnavailableError,
)

DEFAULT_TELNYX_API_BASE = "https://api.telnyx.com"


class _CommonTelnyx:
"""Shared Telnyx HTTP helpers."""

@property
def _invoke_error_mapping(self) -> dict[type[InvokeError], list[type[Exception]]]:
return {
InvokeConnectionError: [requests.ConnectionError, requests.Timeout],
InvokeServerUnavailableError: [requests.HTTPError],
InvokeRateLimitError: [],
InvokeAuthorizationError: [],
InvokeBadRequestError: [requests.RequestException],
}

def _get_api_base(self, credentials: Mapping[str, Any]) -> str:
base = (
credentials.get("telnyx_api_base")
or credentials.get("api_base")
or DEFAULT_TELNYX_API_BASE
)
return str(base).strip().rstrip("/") or DEFAULT_TELNYX_API_BASE

def _build_url(self, credentials: Mapping[str, Any], path: str) -> str:
base = self._get_api_base(credentials)
return urljoin(base + "/", path.lstrip("/"))

def _get_api_key(self, credentials: Mapping[str, Any]) -> str:
api_key = credentials.get("telnyx_api_key") or credentials.get("api_key")
if not api_key:
raise InvokeAuthorizationError("Telnyx API key is required")
return str(api_key)

def _get_headers(
self,
credentials: Mapping[str, Any],
*,
json_content: bool = True,
extra: Mapping[str, str] | None = None,
) -> dict[str, str]:
headers = {
"Authorization": f"Bearer {self._get_api_key(credentials)}",
}
if json_content:
headers["Content-Type"] = "application/json"
if extra:
headers.update(extra)
return headers

def _post_json(
self,
credentials: Mapping[str, Any],
path: str,
payload: Mapping[str, Any],
*,
stream: bool = False,
timeout: tuple[int, int] = (10, 300),
) -> requests.Response:
response = requests.post(
self._build_url(credentials, path),
headers=self._get_headers(credentials),
json=dict(payload),
timeout=timeout,
stream=stream,
)
self._raise_for_response(response)
return response

def _raise_for_response(self, response: requests.Response) -> None:
if 200 <= response.status_code < 300:
return
message = self._response_error_message(response)
if response.status_code in {401, 403}:
raise InvokeAuthorizationError(message)
if response.status_code == 429:
raise InvokeRateLimitError(message)
if response.status_code in {408, 409, 425}:
raise InvokeConnectionError(message)
if response.status_code >= 500:
raise InvokeServerUnavailableError(message)
if 400 <= response.status_code < 500:
raise InvokeBadRequestError(message)
raise InvokeError(message)

@staticmethod
def _response_error_message(response: requests.Response) -> str:
try:
payload = response.json()
except Exception:
text = getattr(response, "text", "") or ""
return f"Telnyx API request failed with status {response.status_code}: {text}"

if isinstance(payload, dict):
errors = payload.get("errors")
if errors:
return f"Telnyx API request failed with status {response.status_code}: {errors}"
error = payload.get("error")
if isinstance(error, dict):
return f"Telnyx API request failed with status {response.status_code}: {error.get('message') or error}"
if error:
return f"Telnyx API request failed with status {response.status_code}: {error}"
message = payload.get("message")
if message:
return f"Telnyx API request failed with status {response.status_code}: {message}"
return f"Telnyx API request failed with status {response.status_code}: {json.dumps(payload)}"
1 change: 1 addition & 0 deletions models/telnyx/models/llm/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Telnyx LLM models
18 changes: 18 additions & 0 deletions models/telnyx/models/llm/_position.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
- moonshotai/Kimi-K2.6
- zai-org/GLM-5.1-FP8
- MiniMaxAI/MiniMax-M2.7
- Qwen/Qwen3-235B-A22B
- moonshotai/Kimi-K2.5
- openai/gpt-5.2
- openai/gpt-5.1
- openai/gpt-5
- openai/gpt-4.1
- openai/gpt-4o
- openai/gpt-4o-mini
- anthropic/claude-haiku-4-5
- google/gemini-2.5-flash
- Groq/gpt-oss-120b
- meta-llama/Llama-3.3-70B-Instruct
- meta-llama/Meta-Llama-3.1-70B-Instruct
- meta-llama/Meta-Llama-3.1-8B-Instruct
- google/gemma-2b-it
38 changes: 38 additions & 0 deletions models/telnyx/models/llm/anthropic-claude-haiku-4-5.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
model: anthropic/claude-haiku-4-5
label:
en_US: anthropic/claude-haiku-4-5
model_type: llm
features:
- tool-call
model_properties:
mode: chat
context_size: 400000
parameter_rules:
- name: max_tokens
use_template: max_tokens
type: int
default: 512
min: 1
max: 32768
required: true
- name: temperature
use_template: temperature
type: float
default: 0.7
min: 0
max: 1
precision: 2
required: false
- name: top_p
use_template: top_p
type: float
default: 0.9
min: 0
max: 1
precision: 2
required: false
pricing:
input: '0'
output: '0'
unit: '0.001'
currency: USD
38 changes: 38 additions & 0 deletions models/telnyx/models/llm/google-gemini-2.5-flash.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
model: google/gemini-2.5-flash
label:
en_US: google/gemini-2.5-flash
model_type: llm
features:
- tool-call
model_properties:
mode: chat
context_size: 400000
parameter_rules:
- name: max_tokens
use_template: max_tokens
type: int
default: 512
min: 1
max: 32768
required: true
- name: temperature
use_template: temperature
type: float
default: 0.7
min: 0
max: 1
precision: 2
required: false
- name: top_p
use_template: top_p
type: float
default: 0.9
min: 0
max: 1
precision: 2
required: false
pricing:
input: '0'
output: '0'
unit: '0.001'
currency: USD
38 changes: 38 additions & 0 deletions models/telnyx/models/llm/google-gemma-2b-it.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
model: google/gemma-2b-it
label:
en_US: google/gemma-2b-it
model_type: llm
features:
- tool-call
model_properties:
mode: chat
context_size: 8192
parameter_rules:
- name: max_tokens
use_template: max_tokens
type: int
default: 512
min: 1
max: 8192
required: true
- name: temperature
use_template: temperature
type: float
default: 0.7
min: 0
max: 1
precision: 2
required: false
- name: top_p
use_template: top_p
type: float
default: 0.9
min: 0
max: 1
precision: 2
required: false
pricing:
input: '0'
output: '0'
unit: '0.001'
currency: USD
38 changes: 38 additions & 0 deletions models/telnyx/models/llm/groq-gpt-oss-120b.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
model: Groq/gpt-oss-120b
label:
en_US: Groq/gpt-oss-120b
model_type: llm
features:
- tool-call
model_properties:
mode: chat
context_size: 400000
parameter_rules:
- name: max_tokens
use_template: max_tokens
type: int
default: 512
min: 1
max: 32768
required: true
- name: temperature
use_template: temperature
type: float
default: 0.7
min: 0
max: 1
precision: 2
required: false
- name: top_p
use_template: top_p
type: float
default: 0.9
min: 0
max: 1
precision: 2
required: false
pricing:
input: '0'
output: '0'
unit: '0.001'
currency: USD
Loading
Loading