Skip to content

Commit 0d72abe

Browse files
committed
Fix gateway/webhook/run bugs, align types with REST API, increase test coverage
1 parent 1bd31f5 commit 0d72abe

37 files changed

Lines changed: 2637 additions & 656 deletions

src/api.jl

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -204,26 +204,4 @@ function mlfdelete_v3(mlf, endpoint; kwargs...)
204204
@error error_message
205205
throw(ErrorException(error_message))
206206
end
207-
end
208-
209-
"""
210-
mlfpatch_v3(mlf, endpoint; kwargs...)
211-
212-
Performs a HTTP PATCH to the specified endpoint using API version 3.0. kwargs are converted to JSON and become
213-
the PATCH body.
214-
"""
215-
function mlfpatch_v3(mlf, endpoint; kwargs...)
216-
apiuri = uri_v3(mlf, endpoint;)
217-
apiheaders = headers(mlf, Dict("Content-Type" => "application/json"))
218-
body = JSON.json(kwargs)
219-
220-
try
221-
response = HTTP.patch(apiuri, apiheaders, body)
222-
return response.body |> String |> JSON.parse
223-
catch e
224-
error_response = e.response.body |> String |> JSON.parse
225-
error_message = "$(error_response["error_code"]) - $(error_response["message"])"
226-
@error error_message
227-
throw(ErrorException(error_message))
228-
end
229-
end
207+
end

src/services/experiment.jl

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,21 @@ The ID of the newly created [`Experiment`](@ref).
2121
function createexperiment(instance::MLFlow, name::String;
2222
artifact_location::Union{String,Missing}=missing,
2323
tags::MLFlowUpsertData{Tag}=Tag[])::String
24-
result = mlfpost(instance, "experiments/create"; name=name,
25-
artifact_location=artifact_location, tags=parse(Tag, tags))
26-
return result["experiment_id"]
24+
try
25+
result = mlfpost(instance, "experiments/create"; name=name,
26+
artifact_location=artifact_location, tags=parse(Tag, tags))
27+
return result["experiment_id"]
28+
catch e
29+
# MLflow with basic-auth may return RESOURCE_ALREADY_EXISTS due to a
30+
# permission auto-creation race condition, even though the experiment was
31+
# successfully created. Recover by fetching the experiment by name.
32+
if e isa ErrorException && occursin("Experiment permission", e.msg) &&
33+
occursin("already exists", e.msg)
34+
experiment = getexperimentbyname(instance, name)
35+
return experiment.experiment_id
36+
end
37+
rethrow()
38+
end
2739
end
2840

2941
"""
@@ -163,12 +175,19 @@ updateexperiment(instance::MLFlow, experiment::Experiment, new_name::String)::Bo
163175
- The next page token if there are more results.
164176
"""
165177
function searchexperiments(instance::MLFlow; max_results::Int64=20000,
166-
page_token::String="", filter::String="", order_by::Array{String}=String[],
178+
page_token::Union{String,Missing}=missing, filter::Union{String,Missing}=missing,
179+
order_by::Array{String}=String[],
167180
view_type::ViewType.ViewTypeEnum=ViewType.ACTIVE_ONLY)::Tuple{Array{Experiment},Union{String,Nothing}}
168-
parameters = (; max_results, page_token, filter, :view_type => view_type |> Integer)
181+
parameters = Dict{Symbol,Any}(:max_results => max_results, :view_type => view_type |> Integer)
169182

170-
if order_by |> !isempty
171-
parameters = (; order_by, parameters...)
183+
if !ismissing(page_token) && !isempty(page_token)
184+
parameters[:page_token] = page_token
185+
end
186+
if !ismissing(filter) && !isempty(filter)
187+
parameters[:filter] = filter
188+
end
189+
if !isempty(order_by)
190+
parameters[:order_by] = order_by
172191
end
173192

174193
result = mlfget(instance, "experiments/search"; parameters...)

src/services/gateway.jl

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -191,24 +191,31 @@ end
191191

192192
"""
193193
updategatewaymodeldefinition(instance::MLFlow, model_definition_id::String;
194-
model_name::Union{String,Missing}=missing, updated_by::Union{String,Missing}=missing)
194+
name::Union{String,Missing}=missing, secret_id::Union{String,Missing}=missing,
195+
model_name::Union{String,Missing}=missing, provider::Union{String,Missing}=missing,
196+
updated_by::Union{String,Missing}=missing)
195197
196198
Update a model definition.
197199
198200
# Arguments
199201
- `instance`: [`MLFlow`](@ref) configuration.
200202
- `model_definition_id`: ID of the model definition to update.
203+
- `name`: Optional new name.
204+
- `secret_id`: Optional new secret ID.
201205
- `model_name`: Optional new model name.
206+
- `provider`: Optional new provider.
202207
- `updated_by`: Username of the updater.
203208
204209
# Returns
205210
An instance of type [`GatewayModelDefinition`](@ref).
206211
"""
207212
function updategatewaymodeldefinition(instance::MLFlow, model_definition_id::String;
208-
model_name::Union{String,Missing}=missing, updated_by::Union{String,Missing}=missing)::GatewayModelDefinition
213+
name::Union{String,Missing}=missing, secret_id::Union{String,Missing}=missing,
214+
model_name::Union{String,Missing}=missing, provider::Union{String,Missing}=missing,
215+
updated_by::Union{String,Missing}=missing)::GatewayModelDefinition
209216
result = mlfpost_v3(instance, "gateway/model-definitions/update";
210-
model_definition_id=model_definition_id, model_name=model_name,
211-
updated_by=updated_by)
217+
model_definition_id=model_definition_id, name=name, secret_id=secret_id,
218+
model_name=model_name, provider=provider, updated_by=updated_by)
212219
return result["model_definition"] |> GatewayModelDefinition
213220
end
214221

@@ -289,26 +296,47 @@ end
289296

290297
"""
291298
updategatewayendpoint(instance::MLFlow, endpoint_id::String;
292-
name::Union{String,Missing}=missing, config=missing,
299+
name::Union{String,Missing}=missing,
300+
model_configs::Union{Array,Missing}=missing,
301+
routing_strategy::Union{String,Missing}=missing,
302+
fallback_config::Union{Dict,Missing}=missing,
303+
experiment_id::Union{String,Missing}=missing,
304+
usage_tracking::Union{Bool,Missing}=missing,
293305
updated_by::Union{String,Missing}=missing)
294306
295307
Update a gateway endpoint.
296308
297309
# Arguments
298310
- `instance`: [`MLFlow`](@ref) configuration.
299311
- `endpoint_id`: ID of the endpoint to update.
300-
- `name`: Optional new name.
301-
- `config`: Optional new configuration.
312+
- `name`: Optional new name for the endpoint.
313+
- `model_configs`: Optional new list of model configurations (replaces all existing).
314+
- `routing_strategy`: Optional new routing strategy.
315+
- `fallback_config`: Optional fallback configuration.
316+
- `experiment_id`: Optional experiment ID for tracing.
317+
- `usage_tracking`: Whether to enable usage tracking.
302318
- `updated_by`: Username of the updater.
303319
304320
# Returns
305321
An instance of type [`GatewayEndpoint`](@ref).
306322
"""
307323
function updategatewayendpoint(instance::MLFlow, endpoint_id::String;
308-
name::Union{String,Missing}=missing, config=missing,
324+
name::Union{String,Missing}=missing,
325+
model_configs::Union{Array,Missing}=missing,
326+
routing_strategy::Union{String,Missing}=missing,
327+
fallback_config::Union{Dict,Missing}=missing,
328+
experiment_id::Union{String,Missing}=missing,
329+
usage_tracking::Union{Bool,Missing}=missing,
309330
updated_by::Union{String,Missing}=missing)::GatewayEndpoint
310-
result = mlfpost_v3(instance, "gateway/endpoints/update";
311-
endpoint_id=endpoint_id, name=name, config=config, updated_by=updated_by)
331+
params = Dict{Symbol,Any}(:endpoint_id => endpoint_id)
332+
!ismissing(name) && (params[:name] = name)
333+
!ismissing(model_configs) && (params[:model_configs] = model_configs)
334+
!ismissing(routing_strategy) && (params[:routing_strategy] = routing_strategy)
335+
!ismissing(fallback_config) && (params[:fallback_config] = fallback_config)
336+
!ismissing(experiment_id) && (params[:experiment_id] = experiment_id)
337+
!ismissing(usage_tracking) && (params[:usage_tracking] = usage_tracking)
338+
!ismissing(updated_by) && (params[:updated_by] = updated_by)
339+
result = mlfpost_v3(instance, "gateway/endpoints/update"; params...)
312340
return result["endpoint"] |> GatewayEndpoint
313341
end
314342

@@ -367,14 +395,14 @@ Attach a model to a gateway endpoint.
367395
- `created_by`: Username of the creator.
368396
369397
# Returns
370-
An instance of type [`GatewayEndpoint`](@ref).
398+
An instance of type [`GatewayEndpointModelMapping`](@ref).
371399
"""
372400
function attachmodeltogatewayendpoint(instance::MLFlow, endpoint_id::String,
373-
model_config::Dict{String,Any}; created_by::Union{String,Missing}=missing)::GatewayEndpoint
401+
model_config::Dict{String,Any}; created_by::Union{String,Missing}=missing)::GatewayEndpointModelMapping
374402
params = Dict{Symbol,Any}(:endpoint_id => endpoint_id, :model_config => model_config)
375403
!ismissing(created_by) && (params[:created_by] = created_by)
376404
result = mlfpost_v3(instance, "gateway/endpoints/models/attach"; params...)
377-
return result["endpoint"] |> GatewayEndpoint
405+
return result["mapping"] |> GatewayEndpointModelMapping
378406
end
379407

380408
"""
@@ -393,7 +421,7 @@ Detach a model from a gateway endpoint.
393421
"""
394422
function detachmodelfromgatewayendpoint(instance::MLFlow, endpoint_id::String,
395423
model_definition_id::String)::Bool
396-
mlfdelete_v3(instance, "gateway/endpoints/models/detach";
424+
mlfpost_v3(instance, "gateway/endpoints/models/detach";
397425
endpoint_id=endpoint_id, model_definition_id=model_definition_id)
398426
return true
399427
end

src/services/model_version.jl

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,19 @@ end
112112
- Vector of [`ModelVersion`](@ref) that were found in the [`MLFlow`](@ref) instance.
113113
- The next page token if there are more results.
114114
"""
115-
function searchmodelversions(instance::MLFlow; filter::String="",
115+
function searchmodelversions(instance::MLFlow; filter::Union{String,Missing}=missing,
116116
max_results::Int64=200000, order_by::Array{String}=String[],
117-
page_token::String="")::Tuple{Array{ModelVersion},Union{String,Nothing}}
118-
parameters = (; max_results, page_token, filter)
117+
page_token::Union{String,Missing}=missing)::Tuple{Array{ModelVersion},Union{String,Nothing}}
118+
parameters = Dict{Symbol,Any}(:max_results => max_results)
119119

120-
if order_by |> !isempty
121-
parameters = (; order_by, parameters...)
120+
if !ismissing(filter) && !isempty(filter)
121+
parameters[:filter] = filter
122+
end
123+
if !ismissing(page_token) && !isempty(page_token)
124+
parameters[:page_token] = page_token
125+
end
126+
if !isempty(order_by)
127+
parameters[:order_by] = order_by
122128
end
123129

124130
result = mlfget(instance, "model-versions/search"; parameters...)

src/services/registered_model.jl

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,19 @@ end
108108
- Vector of [`RegisteredModel`](@ref) that were found in the [`MLFlow`](@ref) instance.
109109
- The next page token if there are more results.
110110
"""
111-
function searchregisteredmodels(instance::MLFlow; filter::String="",
111+
function searchregisteredmodels(instance::MLFlow; filter::Union{String,Missing}=missing,
112112
max_results::Int64=100, order_by::Array{String}=String[],
113-
page_token::String="")::Tuple{Array{RegisteredModel},Union{String,Nothing}}
114-
parameters = (; max_results, page_token, filter)
113+
page_token::Union{String,Missing}=missing)::Tuple{Array{RegisteredModel},Union{String,Nothing}}
114+
parameters = Dict{Symbol,Any}(:max_results => max_results)
115115

116-
if order_by |> !isempty
117-
parameters = (; order_by, parameters...)
116+
if !ismissing(filter) && !isempty(filter)
117+
parameters[:filter] = filter
118+
end
119+
if !ismissing(page_token) && !isempty(page_token)
120+
parameters[:page_token] = page_token
121+
end
122+
if !isempty(order_by)
123+
parameters[:order_by] = order_by
118124
end
119125

120126
result = mlfget(instance, "registered-models/search"; parameters...)

src/services/run.jl

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,24 @@ Search for runs that satisfy expressions. Search expressions can use [`Metric`](
170170
- The next page token if there are more results.
171171
"""
172172
function searchruns(instance::MLFlow; experiment_ids::Array{String}=String[],
173-
filter::String="", run_view_type::ViewType.ViewTypeEnum=ViewType.ACTIVE_ONLY,
173+
filter::Union{String,Missing}=missing,
174+
run_view_type::ViewType.ViewTypeEnum=ViewType.ACTIVE_ONLY,
174175
max_results::Int=1000, order_by::Array{String}=String[],
175-
page_token::String="")::Tuple{Array{Run},Union{String,Nothing}}
176-
parameters = (; experiment_ids, filter, :run_view_type => run_view_type |> Integer,
177-
max_results, page_token)
178-
179-
if order_by |> !isempty
180-
parameters = (; order_by, parameters...)
176+
page_token::Union{String,Missing}=missing)::Tuple{Array{Run},Union{String,Nothing}}
177+
parameters = Dict{Symbol,Any}(
178+
:experiment_ids => experiment_ids,
179+
:run_view_type => run_view_type |> Integer,
180+
:max_results => max_results
181+
)
182+
183+
if !ismissing(filter) && !isempty(filter)
184+
parameters[:filter] = filter
185+
end
186+
if !ismissing(page_token) && !isempty(page_token)
187+
parameters[:page_token] = page_token
188+
end
189+
if !isempty(order_by)
190+
parameters[:order_by] = order_by
181191
end
182192

183193
result = mlfpost(instance, "runs/search"; parameters...)
@@ -208,13 +218,14 @@ Update [`Run`](@ref) metadata.
208218
"""
209219
function updaterun(instance::MLFlow, run_id::String;
210220
status::Union{RunStatus.RunStatusEnum,Missing}=missing,
211-
end_time::Union{Int64,Missing}=missing, run_name::Union{String,Missing})::RunInfo
212-
result = mlfpost(instance, "runs/update"; run_id=run_id, status=(status |> Integer),
221+
end_time::Union{Int64,Missing}=missing, run_name::Union{String,Missing}=missing)::RunInfo
222+
result = mlfpost(instance, "runs/update"; run_id=run_id,
223+
status=ismissing(status) ? missing : (status |> Integer),
213224
end_time=end_time, run_name=run_name)
214225
return result["run_info"] |> RunInfo
215226
end
216227
updaterun(instance::MLFlow, run::Run;
217228
status::Union{RunStatus.RunStatusEnum,Missing}=missing,
218-
end_time::Union{Int64,Missing}=missing, run_name::Union{String,Missing})::RunInfo =
229+
end_time::Union{Int64,Missing}=missing, run_name::Union{String,Missing}=missing)::RunInfo =
219230
updaterun(instance, run.info.run_id; status=status, end_time=end_time,
220231
run_name=run_name)

src/services/webhook.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,12 @@ List all webhooks.
6060
- The next page token if there are more results.
6161
"""
6262
function listwebhooks(instance::MLFlow; max_results::Int64=100,
63-
page_token::String="")::Tuple{Array{Webhook},Union{String,Nothing}}
64-
result = mlfget(instance, "webhooks"; max_results=max_results, page_token=page_token)
63+
page_token::Union{String,Missing}=missing)::Tuple{Array{Webhook},Union{String,Nothing}}
64+
parameters = Dict{Symbol,Any}(:max_results => max_results)
65+
if !ismissing(page_token) && !isempty(page_token)
66+
parameters[:page_token] = page_token
67+
end
68+
result = mlfget(instance, "webhooks"; parameters...)
6569
webhooks = get(result, "webhooks", []) |> (x -> [Webhook(y) for y in x])
6670
next_page_token = get(result, "next_page_token", nothing)
6771
return webhooks, next_page_token
@@ -174,5 +178,5 @@ function testwebhook(instance::MLFlow, webhook_id::String;
174178
end
175179

176180
result = mlfpost(instance, "webhooks/$(webhook_id)/test"; kwargs...)
177-
return result["test_result"] |> WebhookTestResult
181+
return result["result"] |> WebhookTestResult
178182
end

src/types/artifact.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ struct FileInfo
1111
is_dir::Bool
1212
file_size::Int64
1313
end
14+
FileInfo(data::AbstractDict) = FileInfo(
15+
data["path"], get(data, "is_dir", false), get(data, "file_size", 0))
1416
Base.show(io::IO, t::FileInfo) = show(io, ShowCase(t, new_lines=true))
1517

1618
"""
@@ -28,6 +30,9 @@ struct MultipartUploadCredential
2830
upload_url::String
2931
headers::Dict{String,String}
3032
end
33+
MultipartUploadCredential(data::AbstractDict) = MultipartUploadCredential(
34+
get(data, "part_number", 0), get(data, "url", ""),
35+
Dict{String,String}(k => v for (k, v) in get(data, "headers", Dict())))
3136

3237
"""
3338
MultipartUploadPart

0 commit comments

Comments
 (0)