dex-connector-cohere 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. dex_connector_cohere-0.1.0/.gitignore +6 -0
  2. dex_connector_cohere-0.1.0/PKG-INFO +51 -0
  3. dex_connector_cohere-0.1.0/README.md +40 -0
  4. dex_connector_cohere-0.1.0/examples/cohere_search/__init__.py +3 -0
  5. dex_connector_cohere-0.1.0/examples/cohere_search/flow.py +129 -0
  6. dex_connector_cohere-0.1.0/examples/cohere_search/worker.py +51 -0
  7. dex_connector_cohere-0.1.0/pyproject.toml +37 -0
  8. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/__init__.py +51 -0
  9. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/_generated/__init__.py +2 -0
  10. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/_generated/config.py +25 -0
  11. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/_generated/connection.py +131 -0
  12. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/_generated/definitions.py +55 -0
  13. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/_generated/mock.py +119 -0
  14. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/_generated/models.py +97 -0
  15. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/_generated/steps.py +128 -0
  16. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/client.py +382 -0
  17. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/coheremock/__init__.py +43 -0
  18. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/connection.py +46 -0
  19. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/dex-connector.json +77 -0
  20. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/models.py +22 -0
  21. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/py.typed +0 -0
  22. dex_connector_cohere-0.1.0/src/dex_connectors/cohere/steps.py +33 -0
  23. dex_connector_cohere-0.1.0/tests/test_conformance.py +32 -0
  24. dex_connector_cohere-0.1.0/tests/test_manifest_contract.py +15 -0
  25. dex_connector_cohere-0.1.0/tests/test_operations.py +170 -0
  26. dex_connector_cohere-0.1.0/uv.lock +932 -0
@@ -0,0 +1,6 @@
1
+ /.dex-local/dexcli
2
+ /.dex-local/dex-server-zd
3
+ /.dex-local/data/
4
+ /.dex-local/cli.log
5
+ /.dex-local/visualize.json
6
+ /.dex-local/visualize.err
@@ -0,0 +1,51 @@
1
+ Metadata-Version: 2.5
2
+ Name: dex-connector-cohere
3
+ Version: 0.1.0
4
+ Summary: Cohere connector for Dex Flows
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: dex-connectors-sdk==0.1.0
8
+ Requires-Dist: dex-python-sdk>=1.28.0
9
+ Requires-Dist: httpx>=0.27
10
+ Description-Content-Type: text/markdown
11
+
12
+ # Cohere connector for Python
13
+
14
+ Install with `pip install dex-connector-cohere==0.1.0`. The package requires
15
+ `dex-connectors-sdk==0.1.0` and `dex-python-sdk>=1.28.0`, and implements the
16
+ manifest Queries `embed` and `rerank` with the same branches, defaults, and
17
+ failure classification as the Go module.
18
+
19
+ ## Operations
20
+
21
+ | Operation | Factory | Happy branch | Optional branches |
22
+ | --- | --- | --- | --- |
23
+ | `embed` | `new_embed_step` | `embedded` | `providerRejected`, `invalidResponse`, `defect` |
24
+ | `rerank` | `new_rerank_step` | `ranked` | `providerRejected`, `invalidResponse`, `defect` |
25
+
26
+ `embed` takes a model, 1 to 96 nonblank texts, and an input type
27
+ (`search_document`, `search_query`, `classification`, or `clustering`) and
28
+ returns one float vector per text. `rerank` takes a model, a query, 1 to 1000
29
+ nonblank documents, and an optional `top_n`, and returns each document's index
30
+ and relevance score in ranked order. Both calls may be billed, so the manifest
31
+ allows three attempts within five minutes. 408, 409, 429, 5xx, and transport
32
+ failures retry; 401 and 498 select `providerRejected` with an authentication
33
+ kind, and 403 with an authorization kind.
34
+
35
+ ## Authorization
36
+
37
+ Create a Dex Web connection named `cohere` as described in the
38
+ [connector README](../README.md#connection). The API key travels only in the
39
+ `Authorization: Bearer` header and is resolved for every attempt.
40
+
41
+ ## Example
42
+
43
+ [`examples/cohere_search`](examples/cohere_search/flow.py) embeds a query and
44
+ then reranks candidate documents. Run the Worker with
45
+ `uv run python -m examples.cohere_search.worker` after `dexcli dev` has loaded
46
+ a project containing the `cohere` connection.
47
+
48
+ ## Tests
49
+
50
+ `coheremock.Mock` scripts both Queries and defaults to the manifest answers.
51
+ Run `uv run pytest tests` for provider parity and manifest contract checks.
@@ -0,0 +1,40 @@
1
+ # Cohere connector for Python
2
+
3
+ Install with `pip install dex-connector-cohere==0.1.0`. The package requires
4
+ `dex-connectors-sdk==0.1.0` and `dex-python-sdk>=1.28.0`, and implements the
5
+ manifest Queries `embed` and `rerank` with the same branches, defaults, and
6
+ failure classification as the Go module.
7
+
8
+ ## Operations
9
+
10
+ | Operation | Factory | Happy branch | Optional branches |
11
+ | --- | --- | --- | --- |
12
+ | `embed` | `new_embed_step` | `embedded` | `providerRejected`, `invalidResponse`, `defect` |
13
+ | `rerank` | `new_rerank_step` | `ranked` | `providerRejected`, `invalidResponse`, `defect` |
14
+
15
+ `embed` takes a model, 1 to 96 nonblank texts, and an input type
16
+ (`search_document`, `search_query`, `classification`, or `clustering`) and
17
+ returns one float vector per text. `rerank` takes a model, a query, 1 to 1000
18
+ nonblank documents, and an optional `top_n`, and returns each document's index
19
+ and relevance score in ranked order. Both calls may be billed, so the manifest
20
+ allows three attempts within five minutes. 408, 409, 429, 5xx, and transport
21
+ failures retry; 401 and 498 select `providerRejected` with an authentication
22
+ kind, and 403 with an authorization kind.
23
+
24
+ ## Authorization
25
+
26
+ Create a Dex Web connection named `cohere` as described in the
27
+ [connector README](../README.md#connection). The API key travels only in the
28
+ `Authorization: Bearer` header and is resolved for every attempt.
29
+
30
+ ## Example
31
+
32
+ [`examples/cohere_search`](examples/cohere_search/flow.py) embeds a query and
33
+ then reranks candidate documents. Run the Worker with
34
+ `uv run python -m examples.cohere_search.worker` after `dexcli dev` has loaded
35
+ a project containing the `cohere` connection.
36
+
37
+ ## Tests
38
+
39
+ `coheremock.Mock` scripts both Queries and defaults to the manifest answers.
40
+ Run `uv run pytest tests` for provider parity and manifest contract checks.
@@ -0,0 +1,3 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
3
+ """Cohere search example Flow."""
@@ -0,0 +1,129 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
3
+ """Runnable Cohere search Flow: embed the query, then rerank candidate documents."""
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import dex
8
+ from dex_connectors.sdk import QueryResult
9
+
10
+ from dex_connectors.cohere import (
11
+ Connection,
12
+ EmbedInput,
13
+ EmbedOutput,
14
+ RankedDocument,
15
+ RerankInput,
16
+ RerankOutput,
17
+ new_embed_step,
18
+ new_rerank_step,
19
+ )
20
+
21
+ FLOW_TYPE = "CohereSearch"
22
+ CONNECTION_NAME = "cohere"
23
+ _EMBED_STEP_TYPE = "EmbedSearchQuery"
24
+ _RERANK_STEP_TYPE = "RerankSearchDocuments"
25
+ _ANNOTATION_GROUP = {"groupId": "cohere-search", "groupLabel": "Cohere search"}
26
+
27
+
28
+ @dataclass(frozen=True, slots=True)
29
+ class SearchRequest:
30
+ """Search query and at least two candidate documents."""
31
+
32
+ query: str
33
+ documents: tuple[str, ...]
34
+
35
+
36
+ @dataclass(frozen=True, slots=True)
37
+ class SearchOutcome:
38
+ """Candidate documents in Cohere's ranked order."""
39
+
40
+ results: tuple[RankedDocument, ...] = ()
41
+
42
+
43
+ _REQUEST = dex.Attribute("cohere-search-request", SearchRequest)
44
+
45
+
46
+ def map_to_embed_input(request: SearchRequest) -> EmbedInput:
47
+ """Embed the query as a search query."""
48
+ return EmbedInput(model="embed-v4.0", texts=(request.query,), input_type="search_query")
49
+
50
+
51
+ def map_to_rerank_input(request: SearchRequest) -> RerankInput:
52
+ """Rerank every candidate document against the query."""
53
+ return RerankInput(model="rerank-v5.0-pro", query=request.query, documents=request.documents)
54
+
55
+
56
+ # dex:group group-id:cohere-search group-label:"Cohere search"
57
+ # dex:explanation text:"Check and store the search request before calling Cohere."
58
+ class _StartRequest(dex.Step[SearchRequest]):
59
+ def get_step_type(self) -> str:
60
+ return "StartCohereSearch"
61
+
62
+ def execute(self, context: dex.Context, input: SearchRequest) -> dex.StepDecision:
63
+ if not input.query.strip() or len(input.documents) < 2:
64
+ return dex.force_fail("query and at least two documents are required")
65
+ _REQUEST.set(context, input)
66
+ return dex.go_to(dex.StepRef(_EMBED_STEP_TYPE), input)
67
+
68
+
69
+ # dex:group group-id:cohere-search group-label:"Cohere search"
70
+ # dex:explanation text:"Continue from the query embedding to the rerank call."
71
+ class _AfterEmbedding(dex.Step[QueryResult[EmbedOutput]]):
72
+ def get_step_type(self) -> str:
73
+ return "AfterCohereEmbedding"
74
+
75
+ def execute(self, context: dex.Context, input: QueryResult[EmbedOutput]) -> dex.StepDecision:
76
+ return dex.go_to(dex.StepRef(_RERANK_STEP_TYPE), _REQUEST.get(context))
77
+
78
+
79
+ # dex:group group-id:cohere-search group-label:"Cohere search"
80
+ # dex:explanation text:"Complete with the ranked documents."
81
+ class _CompleteSearch(dex.Step[QueryResult[RerankOutput]]):
82
+ def get_step_type(self) -> str:
83
+ return "CompleteCohereSearch"
84
+
85
+ def execute(self, context: dex.Context, input: QueryResult[RerankOutput]) -> dex.StepDecision:
86
+ return dex.graceful_complete(SearchOutcome(input.value.results))
87
+
88
+
89
+ class SearchFlow(dex.Flow[SearchRequest]):
90
+ """Embed a query and rerank documents through the static cohere connection."""
91
+
92
+ def __init__(self, connection: Connection) -> None:
93
+ if connection.reference.name != CONNECTION_NAME:
94
+ raise ValueError("Cohere search requires the static cohere connection")
95
+ self._start = _StartRequest()
96
+ self._after_embedding = _AfterEmbedding()
97
+ self._complete = _CompleteSearch()
98
+ self._embed = new_embed_step(
99
+ step_type=_EMBED_STEP_TYPE,
100
+ step_input_type=SearchRequest,
101
+ annotations={**_ANNOTATION_GROUP, "explanation": "Embed the search query."},
102
+ connection=connection,
103
+ connection_name=CONNECTION_NAME,
104
+ map_to_operation_input=map_to_embed_input,
105
+ embedded=self._after_embedding,
106
+ )
107
+ self._rerank = new_rerank_step(
108
+ step_type=_RERANK_STEP_TYPE,
109
+ step_input_type=SearchRequest,
110
+ annotations={**_ANNOTATION_GROUP, "explanation": "Rank the candidate documents."},
111
+ connection=connection,
112
+ connection_name=CONNECTION_NAME,
113
+ map_to_operation_input=map_to_rerank_input,
114
+ ranked=self._complete,
115
+ )
116
+
117
+ def get_flow_type(self) -> str:
118
+ """Return the stable Flow identity used in Dex Web."""
119
+ return FLOW_TYPE
120
+
121
+ def get_steps(self) -> dex.StepList[SearchRequest]:
122
+ """Register the start, both Cohere calls, and their result Steps."""
123
+ return dex.StepList.start_step(self._start).other_steps(
124
+ self._embed, self._after_embedding, self._rerank, self._complete
125
+ )
126
+
127
+ def get_persistence_schema(self) -> dex.PersistenceSchema:
128
+ """Persist the search request between the two calls."""
129
+ return dex.PersistenceSchema.of(_REQUEST)
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
3
+ """Worker entrypoint for the Cohere search Flow."""
4
+
5
+ import asyncio
6
+ import os
7
+ import tempfile
8
+ from pathlib import Path
9
+
10
+ import dex
11
+ from dex_connectors.sdk.projectconfig import load_from_environment
12
+
13
+ from dex_connectors.cohere import new_project_connection
14
+
15
+ from .flow import CONNECTION_NAME, SearchFlow
16
+
17
+
18
+ async def run_worker() -> None:
19
+ """Load the named Dex Web connection and serve searches."""
20
+ project = await load_from_environment()
21
+ connection = None
22
+ cache = None
23
+ try:
24
+ connection = new_project_connection(project, CONNECTION_NAME)
25
+ registry = dex.Registry([SearchFlow(connection)], allow_async_handlers=True)
26
+ cache_directory = os.environ.get(
27
+ "DEX_BLOB_CACHE_DIR", str(Path(tempfile.gettempdir()) / "dex-cohere-search-blobs")
28
+ )
29
+ cache = dex.open_blob_cache(dex.BlobCacheConfig(cache_directory, 1 << 30))
30
+ worker = dex.AsyncWorker(
31
+ registry,
32
+ cache,
33
+ dex.WorkerOptions(server_address=os.environ.get("DEX_FLOW_SERVICE_ADDRESS", "localhost:8801")),
34
+ )
35
+ async with worker:
36
+ await worker.start()
37
+ finally:
38
+ if connection is not None:
39
+ await connection.aclose()
40
+ if cache is not None:
41
+ cache.close()
42
+ await project.close()
43
+
44
+
45
+ def main() -> None:
46
+ """Run until the Worker stops or the process receives an interrupt."""
47
+ asyncio.run(run_worker())
48
+
49
+
50
+ if __name__ == "__main__":
51
+ main()
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "dex-connector-cohere"
7
+ version = "0.1.0"
8
+ description = "Cohere connector for Dex Flows"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ dependencies = ["dex-connectors-sdk==0.1.0", "dex-python-sdk>=1.28.0", "httpx>=0.27"]
13
+
14
+ [dependency-groups]
15
+ dev = ["pytest>=8", "pytest-asyncio>=0.24", "mypy>=1.13", "ruff>=0.7", "pyyaml>=6", "types-PyYAML>=6"]
16
+
17
+ [tool.hatch.build.targets.wheel]
18
+ packages = ["src/dex_connectors"]
19
+
20
+ [tool.pytest.ini_options]
21
+ testpaths = ["tests"]
22
+ pythonpath = [".", "src"]
23
+ asyncio_mode = "auto"
24
+ asyncio_default_fixture_loop_scope = "function"
25
+
26
+ [tool.mypy]
27
+ python_version = "3.11"
28
+ strict = true
29
+ mypy_path = "src"
30
+ explicit_package_bases = true
31
+
32
+ [tool.ruff]
33
+ target-version = "py311"
34
+ line-length = 110
35
+
36
+ [tool.ruff.lint]
37
+ select = ["E", "F", "I", "UP", "B"]
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
3
+ """Cohere embedding and rerank Queries for Dex connector Steps."""
4
+
5
+ from .client import (
6
+ CONNECTOR_ID,
7
+ DEFECT,
8
+ EMBED_DEFINITION,
9
+ EMBEDDED,
10
+ INPUT_TYPES,
11
+ INVALID_RESPONSE,
12
+ PROVIDER_REJECTED,
13
+ RANKED,
14
+ RERANK_DEFINITION,
15
+ Client,
16
+ Config,
17
+ Credentials,
18
+ EmbedOperation,
19
+ RerankOperation,
20
+ )
21
+ from .connection import Connection, new_connection, new_connection_with_operations, new_project_connection
22
+ from .models import EmbedInput, EmbedOutput, RankedDocument, RerankInput, RerankOutput
23
+ from .steps import new_embed_step, new_rerank_step
24
+
25
+ __all__ = [
26
+ "Client",
27
+ "Config",
28
+ "Connection",
29
+ "Credentials",
30
+ "EmbedInput",
31
+ "EmbedOperation",
32
+ "EmbedOutput",
33
+ "RankedDocument",
34
+ "RerankInput",
35
+ "RerankOperation",
36
+ "RerankOutput",
37
+ "new_connection",
38
+ "new_connection_with_operations",
39
+ "new_embed_step",
40
+ "new_project_connection",
41
+ "new_rerank_step",
42
+ "CONNECTOR_ID",
43
+ "DEFECT",
44
+ "EMBED_DEFINITION",
45
+ "EMBEDDED",
46
+ "INPUT_TYPES",
47
+ "INVALID_RESPONSE",
48
+ "PROVIDER_REJECTED",
49
+ "RANKED",
50
+ "RERANK_DEFINITION",
51
+ ]
@@ -0,0 +1,2 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
@@ -0,0 +1,25 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
3
+ # ruff: noqa
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from dex_connectors.sdk import SecretString
8
+
9
+
10
+ @dataclass(frozen=True, slots=True)
11
+ class Config:
12
+
13
+ base_url: str = "https://api.cohere.com"
14
+ timeout_seconds: float = 30
15
+ max_response_bytes: int = 4194304
16
+
17
+ setattr(Config, '__module__', 'dex_connectors.cohere.client')
18
+
19
+
20
+ @dataclass(frozen=True, slots=True)
21
+ class Credentials:
22
+
23
+ api_key: SecretString
24
+
25
+ setattr(Credentials, '__module__', 'dex_connectors.cohere.client')
@@ -0,0 +1,131 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
3
+ # ruff: noqa
4
+
5
+ from typing import Any
6
+
7
+ import httpx
8
+ from dex_connectors.sdk import ConnectionRef, Query, Call, SecretString
9
+ from dex_connectors.sdk.projectconfig import ConnectionKey, LoadedProject
10
+
11
+ from ..client import Client, Config, Credentials
12
+ from .definitions import CONNECTOR_ID, EMBED_DEFINITION, RERANK_DEFINITION
13
+ from ..models import EmbedInput, EmbedOutput, RerankInput, RerankOutput
14
+
15
+ class Connection:
16
+
17
+ __slots__ = ('_embed', '_rerank', '_client', 'reference')
18
+
19
+ def __init__(self,
20
+ reference: ConnectionRef,
21
+ embed: Query[EmbedInput, EmbedOutput],
22
+ rerank: Query[RerankInput, RerankOutput],
23
+ client: Client | None = None,
24
+ ) -> None:
25
+ reference.validate()
26
+ if reference.provider != CONNECTOR_ID or embed is None or rerank is None:
27
+ raise ValueError("Cohere connection provider and operations are required")
28
+ if embed.definition().operation != EMBED_DEFINITION.operation:
29
+ raise ValueError("Cohere connection operation identity differs")
30
+ if rerank.definition().operation != RERANK_DEFINITION.operation:
31
+ raise ValueError("Cohere connection operation identity differs")
32
+ self.reference = reference
33
+ self._embed = embed
34
+ self._rerank = rerank
35
+ self._client = client
36
+
37
+ def embed(self) -> Query[EmbedInput, EmbedOutput]:
38
+ return self._embed
39
+
40
+ def rerank(self) -> Query[RerankInput, RerankOutput]:
41
+ return self._rerank
42
+
43
+ async def aclose(self) -> None:
44
+ if self._client is not None:
45
+ await self._client.aclose()
46
+
47
+ def __repr__(self) -> str:
48
+ return "cohere.Connection([REDACTED])"
49
+
50
+ def __reduce__(self) -> tuple[object, ...]:
51
+ raise TypeError("Cohere connections cannot be serialized")
52
+
53
+ setattr(Connection, '__module__', 'dex_connectors.cohere.connection')
54
+
55
+
56
+ def new_connection(client: Client, reference: ConnectionRef) -> Connection:
57
+ if client is None:
58
+ raise ValueError("Cohere client is required")
59
+ return Connection(reference, client.embed(), client.rerank(), client)
60
+
61
+ setattr(new_connection, '__module__', 'dex_connectors.cohere.connection')
62
+
63
+
64
+ def new_connection_with_operations(
65
+ embed: Query[EmbedInput, EmbedOutput],
66
+ rerank: Query[RerankInput, RerankOutput],
67
+ reference: ConnectionRef,
68
+ ) -> Connection:
69
+ return Connection(reference, embed, rerank)
70
+
71
+ setattr(new_connection_with_operations, '__module__', 'dex_connectors.cohere.connection')
72
+
73
+
74
+ class _ProjectCredentialSource:
75
+
76
+ def __init__(self, project: LoadedProject, key: ConnectionKey) -> None:
77
+ self.project = project
78
+ self.key = key
79
+
80
+ async def resolve(self, call: Call) -> Credentials:
81
+ material = await self.project.resolve_credential(self.key, str(call.id))
82
+ api_key = material.get("api_key")
83
+ if not isinstance(api_key, str) or not api_key:
84
+ raise ValueError("Cohere credential api_key is required")
85
+ return Credentials(SecretString(api_key))
86
+
87
+
88
+ def new_project_connection(
89
+ project: LoadedProject, connection_name: str, http_client: httpx.AsyncClient | None = None
90
+ ) -> Connection:
91
+ if project is None:
92
+ raise ValueError("Cohere connection requires loaded project configuration")
93
+ reference = ConnectionRef(CONNECTOR_ID, connection_name)
94
+ reference.validate()
95
+ key = ConnectionKey(CONNECTOR_ID, connection_name)
96
+ matching = [
97
+ entry
98
+ for entry in project.configuration.connections
99
+ if entry.connector_id == CONNECTOR_ID and entry.connection_name == connection_name
100
+ ]
101
+ if len(matching) != 1:
102
+ raise ValueError("Cohere project connection is missing or duplicated")
103
+ if matching[0].provider != CONNECTOR_ID:
104
+ raise ValueError("Cohere project connection provider differs")
105
+ config = _decode_config(matching[0].configuration)
106
+ client = Client(config, _ProjectCredentialSource(project, key), http_client)
107
+ return new_connection(client, reference)
108
+
109
+ setattr(new_project_connection, '__module__', 'dex_connectors.cohere.connection')
110
+
111
+
112
+ def _decode_config(value: dict[str, Any]) -> Config:
113
+ if set(value) - {"baseUrl", "timeout", "maxResponseBytes"}:
114
+ raise ValueError("Cohere configuration has unknown fields")
115
+ config_arguments: dict[str, Any] = {}
116
+ if "baseUrl" in value:
117
+ base_url = value["baseUrl"]
118
+ if not (isinstance(base_url, str)):
119
+ raise ValueError("Cohere configuration baseUrl has an invalid type")
120
+ config_arguments['base_url'] = base_url
121
+ if "timeout" in value:
122
+ timeout = value["timeout"]
123
+ if not (type(timeout) is int):
124
+ raise ValueError("Cohere configuration timeout has an invalid type")
125
+ config_arguments['timeout_seconds'] = timeout / 1_000_000_000
126
+ if "maxResponseBytes" in value:
127
+ max_response_bytes = value["maxResponseBytes"]
128
+ if not (type(max_response_bytes) is int):
129
+ raise ValueError("Cohere configuration maxResponseBytes has an invalid type")
130
+ config_arguments['max_response_bytes'] = max_response_bytes
131
+ return Config(**config_arguments)
@@ -0,0 +1,55 @@
1
+ # Copyright (c) 2026 Super Durable
2
+ # SPDX-License-Identifier: MIT
3
+ # ruff: noqa
4
+
5
+ from datetime import timedelta
6
+
7
+ from dex_connectors.sdk import (
8
+ BranchDefinition,
9
+ MutationDefinition,
10
+ OperationRef,
11
+ QueryDefinition,
12
+ RetryPolicy,
13
+ StepDefaults,
14
+ default_connector_step_execute_retry_policy,
15
+ )
16
+
17
+ CONNECTOR_ID = "cohere"
18
+ PROVIDER = "cohere"
19
+ EMBED_OPERATION_ID = "embed"
20
+ RERANK_OPERATION_ID = "rerank"
21
+ DEFECT = "defect"
22
+ EMBEDDED = "embedded"
23
+ INVALID_RESPONSE = "invalidResponse"
24
+ PROVIDER_REJECTED = "providerRejected"
25
+ RANKED = "ranked"
26
+
27
+ EMBED_DEFINITION = QueryDefinition(
28
+ OperationRef(CONNECTOR_ID, EMBED_OPERATION_ID),
29
+ (
30
+ BranchDefinition(EMBEDDED, "Cohere returned one float vector per text.", False),
31
+ BranchDefinition(PROVIDER_REJECTED, "Cohere conclusively rejected the embedding request.", True),
32
+ BranchDefinition(INVALID_RESPONSE, "Cohere returned malformed or oversized embedding data.", True),
33
+ BranchDefinition(DEFECT, "Local input or connection credentials are invalid.", True),
34
+ ),
35
+ StepDefaults(
36
+ timedelta(seconds=45),
37
+ 2,
38
+ execute_retry=RetryPolicy(timedelta(seconds=2), 2, timedelta(seconds=30), 3, timedelta(seconds=300)),
39
+ ),
40
+ )
41
+
42
+ RERANK_DEFINITION = QueryDefinition(
43
+ OperationRef(CONNECTOR_ID, RERANK_OPERATION_ID),
44
+ (
45
+ BranchDefinition(RANKED, "Cohere returned ranked document indices and scores.", False),
46
+ BranchDefinition(PROVIDER_REJECTED, "Cohere conclusively rejected the rerank request.", True),
47
+ BranchDefinition(INVALID_RESPONSE, "Cohere returned malformed or oversized rerank data.", True),
48
+ BranchDefinition(DEFECT, "Local input or connection credentials are invalid.", True),
49
+ ),
50
+ StepDefaults(
51
+ timedelta(seconds=45),
52
+ 2,
53
+ execute_retry=RetryPolicy(timedelta(seconds=2), 2, timedelta(seconds=30), 3, timedelta(seconds=300)),
54
+ ),
55
+ )