Skip to content

Commit c3bbf6b

Browse files
committed
Bump version. Minor documentation updates. search runs by dict of params.
1 parent 6263758 commit c3bbf6b

File tree

9 files changed

+161
-101
lines changed

9 files changed

+161
-101
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
*.jl.*.cov
22
*.jl.cov
33
*.jl.mem
4-
/Manifest.toml
4+
Manifest.toml
55
/docs/build/
66
mlruns

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "MLFlowClient"
22
uuid = "64a0f543-368b-4a9a-827a-e71edb2a0b83"
33
authors = ["@deyandyankov and contributors"]
4-
version = "0.1.0"
4+
version = "0.2.0"
55

66
[deps]
77
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"

docs/src/index.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@ CurrentModule = MLFlowClient
44

55
# MLFlowClient
66

7-
[MLFlowClient](https://github.com/JuliaAI/MLFlowClient.jl) is a [Julia](https://julialang.org/) package for working with [MLFlow](https://mlflow.org/) using the REST [API v2.0](https://www.mlflow.org/docs/latest/rest-api.html).
8-
9-
`MLFlowClient` allows you to create and manage `MLFlow` experiments, runs, and log metrics and artifacts. If you are not familiar with `MLFlow` and its concepts, please refer to [MLFlow documentation](https://mlflow.org/docs/latest/index.html).
10-
11-
## Limitations
12-
- no authentication support.
13-
- when storing artifacts, the assumption is that MLFlow and this library run on the same server. Artifacts are stored using plain filesystem operations. Therefore, `/mlruns` or the specified `artifact_location` must be accessible to both the MLFlow server (read), and this library (write).
7+
```@docs
8+
MLFlowClient
9+
```
1410

1511
## Installation
1612

docs/src/reference.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ CurrentModule = MLFlowClient
66

77
# Types
88

9+
TODO: Document accessors.
10+
911
```@docs
1012
MLFlow
1113
MLFlowExperiment
@@ -43,3 +45,12 @@ logparam
4345
logmetric
4446
logartifact
4547
```
48+
49+
# Utilities
50+
51+
```@docs
52+
mlfget
53+
mlfpost
54+
uri
55+
generatefilterfromparams
56+
```

src/MLFlowClient.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,17 @@ export
2525
MLFlowExperiment,
2626
MLFlowRunStatus,
2727
MLFlowRunInfo,
28+
get_run_id,
2829
MLFlowRunData,
30+
get_params,
2931
MLFlowRunDataMetric,
30-
MLFlowRun
32+
MLFlowRun,
33+
get_info,
34+
get_data
3135

3236
include("utils.jl")
37+
export
38+
generatefilterfromparams
3339

3440
include("experiments.jl")
3541
export

src/runs.jl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ Searches for runs in an experiment.
118118
119119
# Keywords
120120
- `filter::String`: filter as defined in [MLFlow documentation](https://mlflow.org/docs/latest/rest-api.html#search-runs)
121+
- `filter_params::AbstractDict{K,V}`: if provided, `filter` is automatically generated based on `filter_params` using [`generatefilterfromparams`](@ref). One can only provide either `filter` or `filter_params`, but not both.
121122
- `run_view_type::String`: one of `ACTIVE_ONLY`, `DELETED_ONLY`, or `ALL`.
122123
- `max_results::Integer`: 50,000 by default.
123124
- `order_by::String`: as defined in [MLFlow documentation](https://mlflow.org/docs/latest/rest-api.html#search-runs)
@@ -129,13 +130,23 @@ Searches for runs in an experiment.
129130
"""
130131
function searchruns(mlf::MLFlow, experiment_ids::AbstractVector{<:Integer};
131132
filter::String="",
133+
filter_params::AbstractDict{K,V}=Dict{}(),
132134
run_view_type::String="ACTIVE_ONLY",
133135
max_results::Int64=50000,
134136
order_by::AbstractVector{<:String}=["attribute.start_time"],
135137
page_token::String=""
136-
)
138+
) where {K,V}
137139
endpoint = "runs/search"
138140
run_view_type ["ACTIVE_ONLY", "DELETED_ONLY", "ALL"] || error("Unsupported run_view_type = $run_view_type")
141+
142+
if length(filter_params) > 0 && length(filter) > 0
143+
error("Can only use either filter or filter_params, but not both at the same time.")
144+
end
145+
146+
if length(filter_params) > 0
147+
filter = generatefilterfromparams(filter_params)
148+
end
149+
139150
kwargs = (
140151
experiment_ids=experiment_ids,
141152
filter=filter,
@@ -169,3 +180,7 @@ function searchruns(mlf::MLFlow, experiment_ids::AbstractVector{<:Integer};
169180
end
170181
searchruns(mlf::MLFlow, experiment_id::Integer; kwargs...) =
171182
searchruns(mlf, [experiment_id]; kwargs...)
183+
searchruns(mlf::MLFlow, exp::MLFlowExperiment; kwargs...) =
184+
searchruns(mlf, exp.experiment_id; kwargs...)
185+
searchruns(mlf::MLFlow, exps::AbstractVector{MLFlowExperiment}; kwargs...) =
186+
searchruns(mlf, [getfield.(exps, :experiment_id)]; kwargs...)

src/types.jl

Lines changed: 59 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,18 @@ Base type which defines location and version for MLFlow API service.
1313
- `MLFlow()` - defaults to `MLFlow("http://localhost:5000")`
1414
1515
# Examples
16-
``` julia-repl
17-
julia> mlf = MLFlow()
18-
MLFlow("http://localhost:5000", 2.0)
16+
17+
```@example
18+
mlf = MLFlow()
1919
```
2020
2121
"""
2222
struct MLFlow
2323
baseuri::String
2424
apiversion
25-
MLFlow(baseuri; apiversion=2.0) = new(baseuri, apiversion)
2625
end
27-
MLFlow() = MLFlow("http://localhost:5000")
26+
MLFlow(baseuri; apiversion=2.0) = MLFlow(baseuri, apiversion)
27+
MLFlow() = MLFlow("http://localhost:5000", 2.0)
2828

2929
"""
3030
MLFlowExperiment
@@ -50,18 +50,14 @@ struct MLFlowExperiment
5050
experiment_id::Integer
5151
tags::Any
5252
artifact_location::String
53-
54-
MLFlowExperiment(name, lifecycle_stage, experiment_id, tags, artifact_location) =
55-
new(name, lifecycle_stage, experiment_id, tags, artifact_location)
56-
57-
function MLFlowExperiment(exp::Dict{String,Any})
58-
name = get(exp, "name", missing)
59-
lifecycle_stage = get(exp, "lifecycle_stage", missing)
60-
experiment_id = parse(Int, get(exp, "experiment_id", missing))
61-
tags = get(exp, "tags", missing)
62-
artifact_location = get(exp, "artifact_location", missing)
63-
MLFlowExperiment(name, lifecycle_stage, experiment_id, tags, artifact_location)
64-
end
53+
end
54+
function MLFlowExperiment(exp::Dict{String,Any})
55+
name = get(exp, "name", missing)
56+
lifecycle_stage = get(exp, "lifecycle_stage", missing)
57+
experiment_id = parse(Int, get(exp, "experiment_id", missing))
58+
tags = get(exp, "tags", missing)
59+
artifact_location = get(exp, "artifact_location", missing)
60+
MLFlowExperiment(name, lifecycle_stage, experiment_id, tags, artifact_location)
6561
end
6662

6763

@@ -79,7 +75,6 @@ Represents the status of an MLFlow Run.
7975
"""
8076
struct MLFlowRunStatus
8177
status::String
82-
8378
function MLFlowRunStatus(status::String)
8479
acceptable_statuses = ["RUNNING", "SCHEDULED", "FINISHED", "FAILED", "KILLED"]
8580
status acceptable_statuses || error("Invalid status $status - choose one of $acceptable_statuses")
@@ -114,39 +109,24 @@ struct MLFlowRunInfo
114109
end_time::Union{Int64,Missing}
115110
artifact_uri::String
116111
lifecycle_stage::String
117-
118-
function MLFlowRunInfo(run_id, experiment_id, status, start_time, end_time, artifact_uri, lifecycle_stage)
119-
new(run_id, experiment_id, status, start_time, end_time, artifact_uri, lifecycle_stage)
120-
end
121-
122-
function MLFlowRunInfo(info::Dict{String,Any})
123-
run_id = get(info, "run_id", missing)
124-
experiment_id = get(info, "experiment_id", missing)
125-
status = get(info, "status", missing)
126-
start_time = get(info, "start_time", missing)
127-
end_time = get(info, "end_time", missing)
128-
artifact_uri = get(info, "artifact_uri", "")
129-
lifecycle_stage = get(info, "lifecycle_stage", "")
130-
131-
if !ismissing(experiment_id)
132-
experiment_id = parse(Int64, experiment_id)
133-
end
134-
135-
if !ismissing(status)
136-
status = MLFlowRunStatus(status)
137-
end
138-
139-
if !ismissing(start_time)
140-
start_time = parse(Int64, start_time)
141-
end
142-
143-
if !ismissing(end_time)
144-
end_time = parse(Int64, end_time)
145-
end
146-
147-
MLFlowRunInfo(run_id, experiment_id, status, start_time, end_time, artifact_uri, lifecycle_stage)
148-
end
149112
end
113+
function MLFlowRunInfo(info::Dict{String,Any})
114+
run_id = get(info, "run_id", missing)
115+
experiment_id = get(info, "experiment_id", missing)
116+
status = get(info, "status", missing)
117+
start_time = get(info, "start_time", missing)
118+
end_time = get(info, "end_time", missing)
119+
artifact_uri = get(info, "artifact_uri", "")
120+
lifecycle_stage = get(info, "lifecycle_stage", "")
121+
122+
experiment_id = ismissing(experiment_id) ? experiment_id : parse(Int64, experiment_id)
123+
status = ismissing(status) ? status : MLFlowRunStatus(status)
124+
start_time = ismissing(start_time) ? start_time : parse(Int64, start_time)
125+
end_time = ismissing(end_time) ? end_time : parse(Int64, end_time)
126+
127+
MLFlowRunInfo(run_id, experiment_id, status, start_time, end_time, artifact_uri, lifecycle_stage)
128+
end
129+
get_run_id(runinfo::MLFlowRunInfo) = runinfo.run_id
150130

151131
"""
152132
MLFlowRunDataMetric
@@ -169,13 +149,13 @@ struct MLFlowRunDataMetric
169149
value::Float64
170150
step::Int64
171151
timestamp::Int64
172-
function MLFlowRunDataMetric(d::Dict{String,Any})
173-
key = d["key"]
174-
value = d["value"]
175-
step = parse(Int64, d["step"])
176-
timestamp = parse(Int64, d["timestamp"])
177-
new(key, value, step, timestamp)
178-
end
152+
end
153+
function MLFlowRunDataMetric(d::Dict{String,Any})
154+
key = d["key"]
155+
value = d["value"]
156+
step = parse(Int64, d["step"])
157+
timestamp = parse(Int64, d["timestamp"])
158+
MLFlowRunDataMetric(key, value, step, timestamp)
179159
end
180160

181161

@@ -198,20 +178,21 @@ struct MLFlowRunData
198178
metrics::Vector{MLFlowRunDataMetric}
199179
params::Union{Dict{String,String},Missing}
200180
tags
201-
function MLFlowRunData(data::Dict{String,Any})
202-
metrics = haskey(data, "metrics") ? MLFlowRunDataMetric.(data["metrics"]) : MLFlowRunDataMetric[]
203-
if haskey(data, "params")
204-
params = Dict{String,String}()
205-
for p in data["params"]
206-
params[p["key"]] = p["value"]
207-
end
208-
else
209-
params = Dict{String,String}()
181+
end
182+
function MLFlowRunData(data::Dict{String,Any})
183+
metrics = haskey(data, "metrics") ? MLFlowRunDataMetric.(data["metrics"]) : MLFlowRunDataMetric[]
184+
if haskey(data, "params")
185+
params = Dict{String,String}()
186+
for p in data["params"]
187+
params[p["key"]] = p["value"]
210188
end
211-
tags = haskey(data, "tags") ? data["tags"] : missing
212-
new(metrics, params, tags)
189+
else
190+
params = Dict{String,String}()
213191
end
192+
tags = haskey(data, "tags") ? data["tags"] : missing
193+
MLFlowRunData(metrics, params, tags)
214194
end
195+
get_params(rundata::MLFlowRunData) = rundata.params
215196

216197
"""
217198
MLFlowRun
@@ -233,23 +214,14 @@ Represents an MLFlow run.
233214
struct MLFlowRun
234215
info::Union{MLFlowRunInfo,Missing}
235216
data::Union{MLFlowRunData,Missing}
236-
237-
function MLFlowRun(rundata::MLFlowRunData)
238-
info = missing
239-
new(info, rundata)
240-
end
241-
function MLFlowRun(runinfo::MLFlowRunInfo)
242-
data = missing
243-
new(runinfo, data)
244-
end
245-
function MLFlowRun(info::Dict{String,Any})
246-
info = MLFlowRunInfo(info)
247-
data = missing
248-
new(info, data)
249-
end
250-
function MLFlowRun(info::Dict{String,Any}, data::Dict{String,Any})
251-
info = MLFlowRunInfo(info)
252-
data = MLFlowRunData(data)
253-
new(info, data)
254-
end
255217
end
218+
MLFlowRun(rundata::MLFlowRunData) =
219+
MLFlowRun(missing, rundata)
220+
MLFlowRun(runinfo::MLFlowRunInfo) =
221+
MLFlowRun(runinfo, missing)
222+
MLFlowRun(info::Dict{String,Any}) =
223+
MLFlowRun(MLFlowRunInfo(info), missing)
224+
MLFlowRun(info::Dict{String,Any}, data::Dict{String,Any}) =
225+
MLFlowRun(MLFlowRunInfo(info), MLFlowRunData(data))
226+
get_info(run::MLFlowRun) = run.info
227+
get_data(run::MLFlowRun) = run.data

src/utils.jl

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
Retrieves an URI based on `mlf`, `endpoint`, and, optionally, `query`.
55
66
# Examples
7-
``` julia-repl
8-
julia> MLFlowClient.uri(mlf, "experiments/get", Dict(:experiment_id=>10))
9-
URI("http://localhost/api/2.0/mlflow/experiments/get?experiment_id=10")
7+
```@example
8+
MLFlowClient.uri(mlf, "experiments/get", Dict(:experiment_id=>10))
109
```
1110
"""
1211
function uri(mlf::MLFlow, endpoint="", query=missing)
@@ -48,3 +47,26 @@ function mlfpost(mlf, endpoint; kwargs...)
4847
end
4948
end
5049

50+
"""
51+
generatefilterfromparams(filter_params::AbstractDict{K,V}) where {K,V}
52+
53+
Generates a `filter` string from `filter_params` dictionary.
54+
55+
# Arguments
56+
- `filter_params`: dictionary to use for filter generation.
57+
58+
# Returns
59+
A string that can be passed as `filter` to [`searchruns`](@ref).
60+
61+
# Examples
62+
63+
```@example
64+
generatefilterfromparams(Dict("paramkey1" => "paramvalue1", "paramkey2" => "paramvalue2"))
65+
```
66+
"""
67+
function generatefilterfromparams(filter_params::AbstractDict{K,V}) where {K,V}
68+
length(filter_params) > 0 || return ""
69+
# NOTE: may have issues with escaping.
70+
filters = ["param.\"$(k)\" = \"$(v)\"" for(k, v) filter_params ]
71+
join(filters, " and ")
72+
end

0 commit comments

Comments
 (0)