Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
dynamo
Commits
e0e9f4a2
Commit
e0e9f4a2
authored
Feb 25, 2025
by
Paul Hendricks
Committed by
GitHub
Feb 25, 2025
Browse files
refactor: adds `TryFrom<&str>` and `FromStr` for `Endpoint` (#263)
parent
72064d84
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
197 additions
and
24 deletions
+197
-24
.gitignore
.gitignore
+1
-0
applications/llm/tio/src/input/endpoint.rs
applications/llm/tio/src/input/endpoint.rs
+6
-16
applications/llm/tio/src/lib.rs
applications/llm/tio/src/lib.rs
+6
-8
lib/runtime/src/pipeline/error.rs
lib/runtime/src/pipeline/error.rs
+3
-0
lib/runtime/src/protocols.rs
lib/runtime/src/protocols.rs
+181
-0
No files found.
.gitignore
View file @
e0e9f4a2
.idea
.idea
.vs/
.vs/
.vscode/
.vscode/
.helix
[Bb]inlog/
[Bb]inlog/
[Bb][Uu][Ii][Ll][Dd]/
[Bb][Uu][Ii][Ll][Dd]/
[Cc][Mm][Aa][Kk][Ee]/
[Cc][Mm][Aa][Kk][Ee]/
...
...
applications/llm/tio/src/input/endpoint.rs
View file @
e0e9f4a2
...
@@ -27,7 +27,7 @@ use triton_distributed_runtime::pipeline::{
...
@@ -27,7 +27,7 @@ use triton_distributed_runtime::pipeline::{
};
};
use
triton_distributed_runtime
::{
protocols
::
Endpoint
,
DistributedRuntime
,
Runtime
};
use
triton_distributed_runtime
::{
protocols
::
Endpoint
,
DistributedRuntime
,
Runtime
};
use
crate
::
{
EngineConfig
,
ENDPOINT_SCHEME
}
;
use
crate
::
EngineConfig
;
pub
async
fn
run
(
pub
async
fn
run
(
runtime
:
Runtime
,
runtime
:
Runtime
,
...
@@ -38,18 +38,8 @@ pub async fn run(
...
@@ -38,18 +38,8 @@ pub async fn run(
let
distributed
=
DistributedRuntime
::
from_settings
(
runtime
.clone
())
.await
?
;
let
distributed
=
DistributedRuntime
::
from_settings
(
runtime
.clone
())
.await
?
;
let
cancel_token
=
runtime
.primary_token
()
.clone
();
let
cancel_token
=
runtime
.primary_token
()
.clone
();
let
elements
:
Vec
<&
str
>
=
path
.split
(
'/'
)
.collect
();
let
endpoint
:
Endpoint
=
path
.parse
()
?
;
if
elements
.len
()
!=
3
{
anyhow
::
bail!
(
"An endpoint URL must have format {ENDPOINT_SCHEME}namespace/component/endpoint"
);
}
let
endpoint
=
Endpoint
{
namespace
:
elements
[
0
]
.to_string
(),
component
:
elements
[
1
]
.to_string
(),
name
:
elements
[
2
]
.to_string
(),
};
let
etcd_client
=
distributed
.etcd_client
();
let
etcd_client
=
distributed
.etcd_client
();
let
(
ingress
,
service_name
)
=
match
engine_config
{
let
(
ingress
,
service_name
)
=
match
engine_config
{
...
@@ -89,7 +79,7 @@ pub async fn run(
...
@@ -89,7 +79,7 @@ pub async fn run(
let
model_registration
=
ModelEntry
{
let
model_registration
=
ModelEntry
{
name
:
service_name
.to_string
(),
name
:
service_name
.to_string
(),
endpoint
,
endpoint
:
endpoint
.clone
()
,
};
};
etcd_client
etcd_client
.kv_create
(
.kv_create
(
...
@@ -100,12 +90,12 @@ pub async fn run(
...
@@ -100,12 +90,12 @@ pub async fn run(
.await
?
;
.await
?
;
let
rt_fut
=
distributed
let
rt_fut
=
distributed
.namespace
(
e
lements
[
0
]
)
?
.namespace
(
e
ndpoint
.namespace
)
?
.component
(
e
lements
[
1
]
)
?
.component
(
e
ndpoint
.component
)
?
.service_builder
()
.service_builder
()
.create
()
.create
()
.await
?
.await
?
.endpoint
(
e
lements
[
2
]
)
.endpoint
(
e
ndpoint
.name
)
.endpoint_builder
()
.endpoint_builder
()
.handler
(
ingress
)
.handler
(
ingress
)
.start
();
.start
();
...
...
applications/llm/tio/src/lib.rs
View file @
e0e9f4a2
...
@@ -26,7 +26,7 @@ use triton_distributed_llm::{
...
@@ -26,7 +26,7 @@ use triton_distributed_llm::{
Annotated
,
Annotated
,
},
},
};
};
use
triton_distributed_runtime
::{
component
::
Client
,
DistributedRuntime
};
use
triton_distributed_runtime
::{
component
::
Client
,
protocols
::
Endpoint
,
DistributedRuntime
};
mod
input
;
mod
input
;
mod
opt
;
mod
opt
;
...
@@ -136,17 +136,15 @@ pub async fn run(
...
@@ -136,17 +136,15 @@ pub async fn run(
}
}
}
}
Output
::
Endpoint
(
path
)
=>
{
Output
::
Endpoint
(
path
)
=>
{
let
elements
:
Vec
<&
str
>
=
path
.split
(
'/'
)
.collect
();
let
endpoint
:
Endpoint
=
path
.parse
()
?
;
if
elements
.len
()
!=
3
{
anyhow
::
bail!
(
"An endpoint URL must have format {ENDPOINT_SCHEME}namespace/component/endpoint"
);
}
// This will attempt to connect to NATS and etcd
// This will attempt to connect to NATS and etcd
let
distributed_runtime
=
DistributedRuntime
::
from_settings
(
runtime
.clone
())
.await
?
;
let
distributed_runtime
=
DistributedRuntime
::
from_settings
(
runtime
.clone
())
.await
?
;
let
client
=
distributed_runtime
let
client
=
distributed_runtime
.namespace
(
e
lements
[
0
]
)
?
.namespace
(
e
ndpoint
.namespace
)
?
.component
(
e
lements
[
1
]
)
?
.component
(
e
ndpoint
.component
)
?
.endpoint
(
e
lements
[
2
]
)
.endpoint
(
e
ndpoint
.name
)
.client
::
<
ChatCompletionRequest
,
Annotated
<
ChatCompletionResponseDelta
>>
()
.client
::
<
ChatCompletionRequest
,
Annotated
<
ChatCompletionResponseDelta
>>
()
.await
?
;
.await
?
;
...
...
lib/runtime/src/pipeline/error.rs
View file @
e0e9f4a2
...
@@ -87,6 +87,9 @@ pub enum PipelineError {
...
@@ -87,6 +87,9 @@ pub enum PipelineError {
#[error(
"Generate Error: {0}"
)]
#[error(
"Generate Error: {0}"
)]
GenerateError
(
Error
),
GenerateError
(
Error
),
#[error(
"An endpoint URL must have the format: namespace/component/endpoint"
)]
InvalidEndpointFormat
,
#[error(
"NATS Request Error: {0}"
)]
#[error(
"NATS Request Error: {0}"
)]
NatsRequestError
(
#[from]
NatsError
<
async_nats
::
jetstream
::
context
::
RequestErrorKind
>
),
NatsRequestError
(
#[from]
NatsError
<
async_nats
::
jetstream
::
context
::
RequestErrorKind
>
),
...
...
lib/runtime/src/protocols.rs
View file @
e0e9f4a2
...
@@ -14,6 +14,10 @@
...
@@ -14,6 +14,10 @@
// limitations under the License.
// limitations under the License.
use
serde
::{
Deserialize
,
Serialize
};
use
serde
::{
Deserialize
,
Serialize
};
use
std
::
convert
::
TryFrom
;
use
std
::
str
::
FromStr
;
use
crate
::
pipeline
::
PipelineError
;
pub
mod
annotated
;
pub
mod
annotated
;
...
@@ -25,6 +29,14 @@ pub struct Component {
...
@@ -25,6 +29,14 @@ pub struct Component {
pub
namespace
:
String
,
pub
namespace
:
String
,
}
}
/// Represents an endpoint with a namespace, component, and name.
///
/// An `Endpoint` is defined by a three-part string separated by `/`:
/// - **namespace**
/// - **component**
/// - **name**
///
/// Example format: `"namespace/component/endpoint"`
#[derive(Debug,
Clone,
Serialize,
Deserialize,
Eq,
PartialEq)]
#[derive(Debug,
Clone,
Serialize,
Deserialize,
Eq,
PartialEq)]
pub
struct
Endpoint
{
pub
struct
Endpoint
{
/// Name of the endpoint.
/// Name of the endpoint.
...
@@ -37,6 +49,67 @@ pub struct Endpoint {
...
@@ -37,6 +49,67 @@ pub struct Endpoint {
pub
namespace
:
String
,
pub
namespace
:
String
,
}
}
impl
TryFrom
<&
str
>
for
Endpoint
{
type
Error
=
PipelineError
;
/// Attempts to create an `Endpoint` from a string.
///
/// # Arguments
/// - `path`: A string in the format `"namespace/component/endpoint"`.
///
/// # Errors
/// Returns a `PipelineError::InvalidFormat` if the input string does not
/// have exactly three parts separated by `/`.
///
/// # Examples
/// ```ignore
/// use std::convert::TryFrom;
/// use triton_distributed::protocols::Endpoint;
///
/// let endpoint = Endpoint::try_from("namespace/component/endpoint").unwrap();
/// assert_eq!(endpoint.namespace, "namespace");
/// assert_eq!(endpoint.component, "component");
/// assert_eq!(endpoint.name, "endpoint");
/// ```
fn
try_from
(
path
:
&
str
)
->
Result
<
Self
,
Self
::
Error
>
{
let
elements
:
Vec
<&
str
>
=
path
.split
(
'/'
)
.collect
();
if
elements
.len
()
!=
3
{
return
Err
(
PipelineError
::
InvalidEndpointFormat
);
}
Ok
(
Endpoint
{
namespace
:
elements
[
0
]
.to_string
(),
component
:
elements
[
1
]
.to_string
(),
name
:
elements
[
2
]
.to_string
(),
})
}
}
impl
FromStr
for
Endpoint
{
type
Err
=
PipelineError
;
/// Parses an `Endpoint` from a string using the standard Rust `.parse::<T>()` pattern.
///
/// This is implemented in terms of [`TryFrom<&str>`].
///
/// # Errors
/// Returns an `PipelineError::InvalidFormat` if the input does not match `"namespace/component/endpoint"`.
///
/// # Examples
/// ```ignore
/// use std::str::FromStr;
/// use triton_distributed::protocols::Endpoint;
///
/// let endpoint: Endpoint = "namespace/component/endpoint".parse().unwrap();
/// assert_eq!(endpoint.namespace, "namespace");
/// assert_eq!(endpoint.component, "component");
/// assert_eq!(endpoint.name, "endpoint");
/// ```
fn
from_str
(
s
:
&
str
)
->
Result
<
Self
,
Self
::
Err
>
{
Endpoint
::
try_from
(
s
)
}
}
#[derive(Debug,
Clone,
Serialize,
Deserialize,
Eq,
PartialEq)]
#[derive(Debug,
Clone,
Serialize,
Deserialize,
Eq,
PartialEq)]
#[serde(rename_all
=
"snake_case"
)]
#[serde(rename_all
=
"snake_case"
)]
pub
enum
RouterType
{
pub
enum
RouterType
{
...
@@ -60,6 +133,8 @@ pub struct ModelMetaData {
...
@@ -60,6 +133,8 @@ pub struct ModelMetaData {
#[cfg(test)]
#[cfg(test)]
mod
tests
{
mod
tests
{
use
super
::
*
;
use
super
::
*
;
use
std
::
convert
::
TryFrom
;
use
std
::
str
::
FromStr
;
#[test]
#[test]
fn
test_component_creation
()
{
fn
test_component_creation
()
{
...
@@ -130,4 +205,110 @@ mod tests {
...
@@ -130,4 +205,110 @@ mod tests {
assert_eq!
(
metadata
.component.namespace
,
"test_namespace"
);
assert_eq!
(
metadata
.component.namespace
,
"test_namespace"
);
assert_eq!
(
metadata
.router_type
,
RouterType
::
PushRoundRobin
);
assert_eq!
(
metadata
.router_type
,
RouterType
::
PushRoundRobin
);
}
}
#[test]
fn
test_valid_endpoint_try_from
()
{
let
input
=
"namespace1/component1/endpoint1"
;
let
endpoint
=
Endpoint
::
try_from
(
input
)
.expect
(
"Valid endpoint should parse successfully"
);
assert_eq!
(
endpoint
.namespace
,
"namespace1"
);
assert_eq!
(
endpoint
.component
,
"component1"
);
assert_eq!
(
endpoint
.name
,
"endpoint1"
);
}
#[test]
fn
test_valid_endpoint_from_str
()
{
let
input
=
"namespace2/component2/endpoint2"
;
let
endpoint
=
Endpoint
::
from_str
(
input
)
.expect
(
"Valid endpoint should parse successfully"
);
assert_eq!
(
endpoint
.namespace
,
"namespace2"
);
assert_eq!
(
endpoint
.component
,
"component2"
);
assert_eq!
(
endpoint
.name
,
"endpoint2"
);
}
#[test]
fn
test_valid_endpoint_parse
()
{
let
input
=
"namespace3/component3/endpoint3"
;
let
endpoint
:
Endpoint
=
input
.parse
()
.expect
(
"Valid endpoint should parse successfully"
);
assert_eq!
(
endpoint
.namespace
,
"namespace3"
);
assert_eq!
(
endpoint
.component
,
"component3"
);
assert_eq!
(
endpoint
.name
,
"endpoint3"
);
}
#[test]
fn
test_invalid_endpoint_try_from
()
{
let
input
=
"invalid_endpoint_format"
;
let
result
=
Endpoint
::
try_from
(
input
);
assert
!
(
result
.is_err
(),
"Parsing should fail for an invalid format"
);
assert_eq!
(
result
.unwrap_err
()
.to_string
(),
"An endpoint URL must have the format: namespace/component/endpoint"
);
}
#[test]
fn
test_invalid_endpoint_from_str
()
{
let
input
=
"onlyhas/two"
;
let
result
=
Endpoint
::
from_str
(
input
);
assert
!
(
result
.is_err
(),
"Parsing should fail for an invalid format"
);
assert_eq!
(
result
.unwrap_err
()
.to_string
(),
"An endpoint URL must have the format: namespace/component/endpoint"
);
}
#[test]
fn
test_invalid_endpoint_parse
()
{
let
input
=
"too/many/segments/in/url"
;
let
result
:
Result
<
Endpoint
,
_
>
=
input
.parse
();
assert
!
(
result
.is_err
(),
"Parsing should fail for an invalid format"
);
assert_eq!
(
result
.unwrap_err
()
.to_string
(),
"An endpoint URL must have the format: namespace/component/endpoint"
);
}
#[test]
fn
test_empty_endpoint_string
()
{
let
input
=
""
;
let
result
=
Endpoint
::
try_from
(
input
);
assert
!
(
result
.is_err
(),
"Parsing should fail for an empty string"
);
assert_eq!
(
result
.unwrap_err
()
.to_string
(),
"An endpoint URL must have the format: namespace/component/endpoint"
);
}
#[test]
fn
test_whitespace_endpoint_string
()
{
let
input
=
" "
;
let
result
=
Endpoint
::
try_from
(
input
);
assert
!
(
result
.is_err
(),
"Parsing should fail for a whitespace string"
);
assert_eq!
(
result
.unwrap_err
()
.to_string
(),
"An endpoint URL must have the format: namespace/component/endpoint"
);
}
#[test]
fn
test_leading_trailing_slashes
()
{
let
input
=
"/namespace/component/endpoint/"
;
let
result
=
Endpoint
::
try_from
(
input
);
assert
!
(
result
.is_err
(),
"Parsing should fail for leading/trailing slashes"
);
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment