completion-kit 0.5.9 → 0.5.10
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.
- checksums.yaml +4 -4
- data/app/assets/images/completion_kit/favicon.ico +0 -0
- data/app/assets/images/completion_kit/logo.png +0 -0
- data/app/assets/stylesheets/completion_kit/application.css +35 -7
- data/app/controllers/completion_kit/api_reference_controller.rb +6 -0
- data/app/controllers/completion_kit/datasets_controller.rb +10 -0
- data/app/controllers/completion_kit/mcp_controller.rb +2 -2
- data/app/models/completion_kit/mcp_session.rb +29 -0
- data/app/services/completion_kit/mcp_dispatcher.rb +1 -3
- data/app/views/completion_kit/api_reference/_body.html.erb +46 -22
- data/app/views/completion_kit/api_reference/_resource_card.html.erb +24 -0
- data/app/views/completion_kit/api_reference/_resource_list.html.erb +10 -0
- data/app/views/completion_kit/api_reference/index.html.erb +7 -1
- data/app/views/completion_kit/datasets/show.html.erb +2 -18
- data/app/views/completion_kit/prompts/show.html.erb +8 -26
- data/app/views/completion_kit/runs/_form.html.erb +1 -1
- data/app/views/completion_kit/runs/_table.html.erb +19 -0
- data/app/views/completion_kit/runs/index.html.erb +1 -17
- data/app/views/layouts/completion_kit/application.html.erb +2 -2
- data/db/migrate/20260513000001_create_completion_kit_mcp_sessions.rb +12 -0
- data/lib/completion_kit/engine.rb +2 -1
- data/lib/completion_kit/version.rb +1 -1
- metadata +8 -2
- data/app/assets/images/completion_kit/logo.svg +0 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 64a36635f11bc5109afed180697b763a1c8231347b4912aa452271fd19694a45
|
|
4
|
+
data.tar.gz: 366ef22faa5914db74402b7ae1c9769f50f14ae8204e2144a245b8d29f89b464
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f84d7a7e0bcd5a3a054e84231cb390026dd07ac469b34ff8bb8a10a6f9f77fbbcb53748b4814d7b86aa94d1e67f41f3ad1e9c8eb1907f0ac56388169765174d6
|
|
7
|
+
data.tar.gz: ab627c627f3c88d1ef4a8f14afa0d72d7a92acc83b1107a4ebfeb25dacaf7a04282dfe3cfa5657d3c474171e508dec9944db95da526cbe8b3d94bbd0a9ef32a7
|
|
Binary file
|
|
Binary file
|
|
@@ -126,13 +126,21 @@ form.button_to {
|
|
|
126
126
|
font-weight: 700;
|
|
127
127
|
letter-spacing: 0.02em;
|
|
128
128
|
text-decoration: none;
|
|
129
|
-
color:
|
|
129
|
+
color: #3AD0E6;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
.ck-brand img {
|
|
133
133
|
display: block;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
.ck-brand__name {
|
|
137
|
+
padding-top: 0.75rem;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.ck-brand__kit {
|
|
141
|
+
color: #AFEDF7;
|
|
142
|
+
}
|
|
143
|
+
|
|
136
144
|
.ck-topbar__copy {
|
|
137
145
|
display: none;
|
|
138
146
|
}
|
|
@@ -262,7 +270,7 @@ form.button_to {
|
|
|
262
270
|
.ck-meta-copy,
|
|
263
271
|
.ck-note,
|
|
264
272
|
.ck-hint {
|
|
265
|
-
font-size: 0.
|
|
273
|
+
font-size: 0.9rem;
|
|
266
274
|
line-height: 1.6;
|
|
267
275
|
}
|
|
268
276
|
|
|
@@ -1269,6 +1277,13 @@ tr:hover .ck-chip--publish {
|
|
|
1269
1277
|
color: var(--ck-accent);
|
|
1270
1278
|
}
|
|
1271
1279
|
|
|
1280
|
+
/* the main prompt template block on prompts/show — bigger padding + a
|
|
1281
|
+
touch more line-height since this is the page's primary content */
|
|
1282
|
+
.ck-code--prompt {
|
|
1283
|
+
padding: 1.5rem;
|
|
1284
|
+
line-height: 1.75;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1272
1287
|
.ck-note-box {
|
|
1273
1288
|
background: var(--ck-surface-soft);
|
|
1274
1289
|
border: 1px solid var(--ck-line);
|
|
@@ -1855,7 +1870,18 @@ tr:hover .ck-chip--publish {
|
|
|
1855
1870
|
background: var(--ck-bg-strong);
|
|
1856
1871
|
overflow: auto;
|
|
1857
1872
|
max-height: 60vh;
|
|
1873
|
+
scrollbar-width: thin;
|
|
1874
|
+
scrollbar-color: var(--ck-line-strong) transparent;
|
|
1858
1875
|
}
|
|
1876
|
+
.ck-csv-table-wrap::-webkit-scrollbar { width: 10px; height: 10px; }
|
|
1877
|
+
.ck-csv-table-wrap::-webkit-scrollbar-track { background: transparent; }
|
|
1878
|
+
.ck-csv-table-wrap::-webkit-scrollbar-thumb {
|
|
1879
|
+
background: var(--ck-line-strong);
|
|
1880
|
+
border-radius: 5px;
|
|
1881
|
+
border: 2px solid var(--ck-bg-strong);
|
|
1882
|
+
}
|
|
1883
|
+
.ck-csv-table-wrap::-webkit-scrollbar-thumb:hover { background: var(--ck-muted); }
|
|
1884
|
+
.ck-csv-table-wrap::-webkit-scrollbar-corner { background: transparent; }
|
|
1859
1885
|
|
|
1860
1886
|
.ck-modal__body .ck-csv-table-wrap {
|
|
1861
1887
|
margin-top: 0;
|
|
@@ -2763,8 +2789,10 @@ select.ck-input {
|
|
|
2763
2789
|
|
|
2764
2790
|
/* the metrics field stacks several sub-sections (hint, groups, divider, tag
|
|
2765
2791
|
filter, checkboxes) — give it more vertical breathing room than a plain field,
|
|
2766
|
-
and extra separation from the run-tags field that follows it
|
|
2767
|
-
|
|
2792
|
+
and extra separation from the run-tags field that follows it. Only when the
|
|
2793
|
+
checkboxes are actually present, though — when there are no metrics the field
|
|
2794
|
+
is just "label + warning" and should be a normal compact field. */
|
|
2795
|
+
#metrics-field:has(.ck-metric-checkboxes) {
|
|
2768
2796
|
gap: 0.85rem;
|
|
2769
2797
|
margin-bottom: 1.25rem;
|
|
2770
2798
|
}
|
|
@@ -3571,7 +3599,7 @@ a.ck-metric-group-pill {
|
|
|
3571
3599
|
}
|
|
3572
3600
|
|
|
3573
3601
|
.ck-mcp-tool__desc {
|
|
3574
|
-
font-size: 0.
|
|
3602
|
+
font-size: 0.9rem;
|
|
3575
3603
|
color: var(--ck-muted);
|
|
3576
3604
|
}
|
|
3577
3605
|
|
|
@@ -3595,7 +3623,7 @@ a.ck-metric-group-pill {
|
|
|
3595
3623
|
gap: 0.5rem;
|
|
3596
3624
|
padding: 0.6rem 0.85rem;
|
|
3597
3625
|
font-family: var(--ck-mono);
|
|
3598
|
-
font-size: 0.
|
|
3626
|
+
font-size: 0.9rem;
|
|
3599
3627
|
font-weight: 500;
|
|
3600
3628
|
color: var(--ck-text);
|
|
3601
3629
|
border-bottom: 1px solid var(--ck-line);
|
|
@@ -3669,7 +3697,7 @@ a.ck-metric-group-pill {
|
|
|
3669
3697
|
}
|
|
3670
3698
|
|
|
3671
3699
|
.ck-api-prompt-card__desc {
|
|
3672
|
-
font-size: 0.
|
|
3700
|
+
font-size: 0.9rem;
|
|
3673
3701
|
color: var(--ck-muted);
|
|
3674
3702
|
margin: 0.2rem 0 0;
|
|
3675
3703
|
line-height: 1.4;
|
|
@@ -2,6 +2,12 @@ module CompletionKit
|
|
|
2
2
|
class ApiReferenceController < ApplicationController
|
|
3
3
|
def index
|
|
4
4
|
@published_prompts = Prompt.current_versions.order(name: :asc)
|
|
5
|
+
@recent_runs = Run.includes(:prompt).order(created_at: :desc).limit(10)
|
|
6
|
+
@datasets = Dataset.order(name: :asc)
|
|
7
|
+
@metrics = Metric.order(name: :asc)
|
|
8
|
+
@metric_groups = MetricGroup.includes(:metrics).order(name: :asc)
|
|
9
|
+
@tags = Tag.order(name: :asc)
|
|
10
|
+
@provider_credentials = ProviderCredential.order(:provider)
|
|
5
11
|
@token = CompletionKit.config.api_token
|
|
6
12
|
@base_url = request.base_url + request.script_name
|
|
7
13
|
end
|
|
@@ -9,6 +9,16 @@ module CompletionKit
|
|
|
9
9
|
|
|
10
10
|
def show
|
|
11
11
|
@runs = @dataset.runs.includes(:prompt, :responses).order(created_at: :desc)
|
|
12
|
+
respond_to do |format|
|
|
13
|
+
format.html
|
|
14
|
+
format.csv do
|
|
15
|
+
slug = @dataset.name.to_s.parameterize.presence || "dataset-#{@dataset.id}"
|
|
16
|
+
send_data @dataset.csv_data.to_s,
|
|
17
|
+
type: "text/csv",
|
|
18
|
+
filename: "#{slug}.csv",
|
|
19
|
+
disposition: "attachment"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
12
22
|
end
|
|
13
23
|
|
|
14
24
|
def new
|
|
@@ -17,7 +17,7 @@ module CompletionKit
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
session_id = request.headers["Mcp-Session-Id"]
|
|
20
|
-
unless
|
|
20
|
+
unless McpSession.active?(session_id)
|
|
21
21
|
render json: jsonrpc_error(request_body["id"], -32000, "Session not initialized. Send initialize first."), status: :bad_request
|
|
22
22
|
return
|
|
23
23
|
end
|
|
@@ -40,7 +40,7 @@ module CompletionKit
|
|
|
40
40
|
|
|
41
41
|
def destroy
|
|
42
42
|
session_id = request.headers["Mcp-Session-Id"]
|
|
43
|
-
|
|
43
|
+
McpSession.destroy_session(session_id) if session_id
|
|
44
44
|
head :ok
|
|
45
45
|
end
|
|
46
46
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module CompletionKit
|
|
2
|
+
# MCP session marker — one row per active client session, kept in the
|
|
3
|
+
# database so sessions survive Puma restarts, deploys, and Rails.cache
|
|
4
|
+
# eviction. Expired rows are opportunistically pruned on every new
|
|
5
|
+
# session start, so the table stays bounded by recent activity.
|
|
6
|
+
class McpSession < ApplicationRecord
|
|
7
|
+
self.table_name = "completion_kit_mcp_sessions"
|
|
8
|
+
|
|
9
|
+
SESSION_TTL = 1.hour
|
|
10
|
+
|
|
11
|
+
def self.start!
|
|
12
|
+
prune_expired!
|
|
13
|
+
create!(session_id: SecureRandom.uuid, expires_at: SESSION_TTL.from_now).session_id
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.active?(session_id)
|
|
17
|
+
return false if session_id.blank?
|
|
18
|
+
where(session_id: session_id).where("expires_at > ?", Time.current).exists?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.destroy_session(session_id)
|
|
22
|
+
where(session_id: session_id).delete_all
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.prune_expired!
|
|
26
|
+
where("expires_at < ?", Time.current).delete_all
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -6,10 +6,8 @@ module CompletionKit
|
|
|
6
6
|
PROTOCOL_VERSION = "2025-03-26"
|
|
7
7
|
|
|
8
8
|
def self.initialize_session
|
|
9
|
-
session_id = SecureRandom.uuid
|
|
10
|
-
Rails.cache.write("mcp_session:#{session_id}", true, expires_in: 1.hour)
|
|
11
9
|
{
|
|
12
|
-
session_id:
|
|
10
|
+
session_id: McpSession.start!,
|
|
13
11
|
protocolVersion: PROTOCOL_VERSION,
|
|
14
12
|
serverInfo: {name: "CompletionKit", version: CompletionKit::VERSION},
|
|
15
13
|
capabilities: {tools: {listChanged: false}}
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
token = local_assigns.fetch(:token, "YOUR_TOKEN")
|
|
3
3
|
real_token = local_assigns.fetch(:real_token, nil)
|
|
4
4
|
published_prompts = local_assigns.fetch(:published_prompts, [])
|
|
5
|
+
recent_runs = local_assigns.fetch(:recent_runs, [])
|
|
6
|
+
datasets = local_assigns.fetch(:datasets, [])
|
|
7
|
+
metrics = local_assigns.fetch(:metrics, [])
|
|
8
|
+
metric_groups = local_assigns.fetch(:metric_groups, [])
|
|
9
|
+
tags = local_assigns.fetch(:tags, [])
|
|
10
|
+
provider_credentials = local_assigns.fetch(:provider_credentials, [])
|
|
5
11
|
%>
|
|
6
12
|
<div class="ck-api-tabs">
|
|
7
13
|
<input type="radio" name="ck-api-tab" id="ck-tab-mcp" class="ck-api-tabs__radio" checked>
|
|
@@ -86,28 +92,11 @@
|
|
|
86
92
|
<p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/prompts/:id</p>
|
|
87
93
|
<p class="ck-meta-copy">Get a single prompt by ID.</p>
|
|
88
94
|
</div>
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
<div class="ck-api-prompt-card__top">
|
|
95
|
-
<div>
|
|
96
|
-
<strong class="ck-api-prompt-card__name"><%= p.name %></strong>
|
|
97
|
-
<% if p.description.present? %>
|
|
98
|
-
<p class="ck-api-prompt-card__desc"><%= p.description %></p>
|
|
99
|
-
<% end %>
|
|
100
|
-
</div>
|
|
101
|
-
<span class="ck-chip" style="text-transform: none; flex-shrink: 0;"><%= p.llm_model %></span>
|
|
102
|
-
</div>
|
|
103
|
-
<div class="ck-api-prompt-card__url">
|
|
104
|
-
<code class="ck-endpoint__url" id="prompt_ep_<%= p.id %>"><%= base_url %>/api/v1/prompts/<%= p.slug %></code>
|
|
105
|
-
<button type="button" class="ck-icon-btn" title="Copy endpoint" aria-label="Copy endpoint URL" onclick="navigator.clipboard.writeText(document.getElementById('prompt_ep_<%= p.id %>').textContent)"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" width="14" height="14" aria-hidden="true"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg></button>
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
108
|
-
<% end %>
|
|
109
|
-
</div>
|
|
110
|
-
<% end %>
|
|
95
|
+
<%= render "completion_kit/api_reference/resource_list", title: "Your published prompts",
|
|
96
|
+
items: published_prompts.map { |p|
|
|
97
|
+
{ name: p.name, subtitle: p.description, meta: p.llm_model,
|
|
98
|
+
url: "#{base_url}/api/v1/prompts/#{p.slug}", dom_id: "prompt_ep_#{p.id}" }
|
|
99
|
+
} %>
|
|
111
100
|
<div class="ck-api-endpoint">
|
|
112
101
|
<p class="ck-api-method"><span class="ck-chip ck-chip--soft">PATCH</span> /api/v1/prompts/:id</p>
|
|
113
102
|
<p class="ck-meta-copy">Update a prompt. Accepts same params as create.</p>
|
|
@@ -140,6 +129,11 @@
|
|
|
140
129
|
<p class="ck-meta-copy">Get a run with status, progress, response count, and average score.</p>
|
|
141
130
|
<%= render "completion_kit/api_reference/example", base_url: base_url, token: token, real_token: real_token, cmd: "curl #{base_url}/api/v1/runs/1 \\\n -H \"Authorization: Bearer #{token}\"" %>
|
|
142
131
|
</div>
|
|
132
|
+
<%= render "completion_kit/api_reference/resource_list", title: "Your recent runs",
|
|
133
|
+
items: recent_runs.map { |r|
|
|
134
|
+
{ name: r.name, meta: r.status.to_s.titleize,
|
|
135
|
+
url: "#{base_url}/api/v1/runs/#{r.id}", dom_id: "run_ep_#{r.id}" }
|
|
136
|
+
} %>
|
|
143
137
|
<div class="ck-api-endpoint">
|
|
144
138
|
<p class="ck-api-method"><span class="ck-chip ck-chip--soft">POST</span> /api/v1/runs/:id/generate</p>
|
|
145
139
|
<p class="ck-meta-copy">Start generating responses. Returns 202 Accepted. Poll the run to check progress.</p>
|
|
@@ -167,6 +161,11 @@
|
|
|
167
161
|
<p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> /api/v1/runs/:run_id/responses/:id</p>
|
|
168
162
|
<p class="ck-meta-copy">Get a single response with its review scores and feedback.</p>
|
|
169
163
|
</div>
|
|
164
|
+
<%= render "completion_kit/api_reference/resource_list", title: "Your responses",
|
|
165
|
+
items: recent_runs.first(1).map { |r|
|
|
166
|
+
{ name: "#{r.name} — responses",
|
|
167
|
+
url: "#{base_url}/api/v1/runs/#{r.id}/responses", dom_id: "responses_ep_#{r.id}" }
|
|
168
|
+
} %>
|
|
170
169
|
</div>
|
|
171
170
|
|
|
172
171
|
<div class="ck-api-tabs__panel">
|
|
@@ -186,6 +185,11 @@
|
|
|
186
185
|
<p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> <span class="ck-chip ck-chip--soft">PATCH</span> <span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/datasets/:id</p>
|
|
187
186
|
<p class="ck-meta-copy">Get, update, or delete a dataset.</p>
|
|
188
187
|
</div>
|
|
188
|
+
<%= render "completion_kit/api_reference/resource_list", title: "Your datasets",
|
|
189
|
+
items: datasets.map { |d|
|
|
190
|
+
{ name: d.name, meta: pluralize([d.csv_data.to_s.lines.count - 1, 0].max, "row"),
|
|
191
|
+
url: "#{base_url}/api/v1/datasets/#{d.id}", dom_id: "dataset_ep_#{d.id}" }
|
|
192
|
+
} %>
|
|
189
193
|
</div>
|
|
190
194
|
|
|
191
195
|
<div class="ck-api-tabs__panel">
|
|
@@ -205,6 +209,11 @@
|
|
|
205
209
|
<p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> <span class="ck-chip ck-chip--soft">PATCH</span> <span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/metrics/:id</p>
|
|
206
210
|
<p class="ck-meta-copy">Get, update, or delete a metric.</p>
|
|
207
211
|
</div>
|
|
212
|
+
<%= render "completion_kit/api_reference/resource_list", title: "Your metrics",
|
|
213
|
+
items: metrics.map { |m|
|
|
214
|
+
{ name: m.name, subtitle: m.instruction.presence&.truncate(100),
|
|
215
|
+
url: "#{base_url}/api/v1/metrics/#{m.id}", dom_id: "metric_ep_#{m.id}" }
|
|
216
|
+
} %>
|
|
208
217
|
</div>
|
|
209
218
|
|
|
210
219
|
<div class="ck-api-tabs__panel">
|
|
@@ -223,6 +232,11 @@
|
|
|
223
232
|
<p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> <span class="ck-chip ck-chip--soft">PATCH</span> <span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/metric_groups/:id</p>
|
|
224
233
|
<p class="ck-meta-copy">Get, update, or delete a metric group. PATCH with <code>metric_ids</code> replaces all metric associations.</p>
|
|
225
234
|
</div>
|
|
235
|
+
<%= render "completion_kit/api_reference/resource_list", title: "Your metric groups",
|
|
236
|
+
items: metric_groups.map { |g|
|
|
237
|
+
{ name: g.name, subtitle: g.description.presence, meta: pluralize(g.metric_ids.size, "metric"),
|
|
238
|
+
url: "#{base_url}/api/v1/metric_groups/#{g.id}", dom_id: "metric_group_ep_#{g.id}" }
|
|
239
|
+
} %>
|
|
226
240
|
</div>
|
|
227
241
|
|
|
228
242
|
<div class="ck-api-tabs__panel">
|
|
@@ -243,6 +257,11 @@
|
|
|
243
257
|
<p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> <span class="ck-chip ck-chip--soft">PATCH</span> <span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/tags/:id</p>
|
|
244
258
|
<p class="ck-meta-copy">Get, update, or delete a tag. PATCH accepts <code>name</code>. DELETE returns 204 No Content and removes all taggings for this tag.</p>
|
|
245
259
|
</div>
|
|
260
|
+
<%= render "completion_kit/api_reference/resource_list", title: "Your tags",
|
|
261
|
+
items: tags.map { |t|
|
|
262
|
+
{ name: t.name, meta: t.color,
|
|
263
|
+
url: "#{base_url}/api/v1/tags/#{t.id}", dom_id: "tag_ep_#{t.id}" }
|
|
264
|
+
} %>
|
|
246
265
|
<div class="ck-api-endpoint" style="padding-top: 1rem;">
|
|
247
266
|
<p class="ck-kicker" style="margin-bottom: 0.5rem;">Tagging resources</p>
|
|
248
267
|
<p class="ck-meta-copy">Metrics, prompts, runs, and datasets accept a <code>tag_names</code> array on their create and update endpoints. Passing a name that does not yet exist silently creates the tag. On PATCH, the list replaces all existing tags for that record (omit the field to leave tags unchanged).</p>
|
|
@@ -277,6 +296,11 @@
|
|
|
277
296
|
<p class="ck-api-method"><span class="ck-chip ck-chip--soft">GET</span> <span class="ck-chip ck-chip--soft">PATCH</span> <span class="ck-chip" style="color: var(--ck-danger);">DELETE</span> /api/v1/provider_credentials/:id</p>
|
|
278
297
|
<p class="ck-meta-copy">Get, update, or delete a provider credential.</p>
|
|
279
298
|
</div>
|
|
299
|
+
<%= render "completion_kit/api_reference/resource_list", title: "Your providers",
|
|
300
|
+
items: provider_credentials.map { |pc|
|
|
301
|
+
{ name: ck_provider_label(pc.provider), meta: pluralize(pc.model_count, "model"),
|
|
302
|
+
url: "#{base_url}/api/v1/provider_credentials/#{pc.id}", dom_id: "provider_ep_#{pc.id}" }
|
|
303
|
+
} %>
|
|
280
304
|
</div>
|
|
281
305
|
|
|
282
306
|
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<%
|
|
2
|
+
name = local_assigns.fetch(:name)
|
|
3
|
+
url = local_assigns.fetch(:url)
|
|
4
|
+
dom_id = local_assigns.fetch(:dom_id)
|
|
5
|
+
subtitle = local_assigns[:subtitle]
|
|
6
|
+
meta = local_assigns[:meta]
|
|
7
|
+
%>
|
|
8
|
+
<div class="ck-api-prompt-card">
|
|
9
|
+
<div class="ck-api-prompt-card__top">
|
|
10
|
+
<div>
|
|
11
|
+
<strong class="ck-api-prompt-card__name"><%= name %></strong>
|
|
12
|
+
<% if subtitle.present? %>
|
|
13
|
+
<p class="ck-api-prompt-card__desc"><%= subtitle %></p>
|
|
14
|
+
<% end %>
|
|
15
|
+
</div>
|
|
16
|
+
<% if meta.present? %>
|
|
17
|
+
<span class="ck-chip" style="text-transform: none; flex-shrink: 0;"><%= meta %></span>
|
|
18
|
+
<% end %>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="ck-api-prompt-card__url">
|
|
21
|
+
<code class="ck-endpoint__url" id="<%= dom_id %>"><%= url %></code>
|
|
22
|
+
<button type="button" class="ck-icon-btn" title="Copy endpoint" aria-label="Copy endpoint URL" onclick="navigator.clipboard.writeText(document.getElementById('<%= dom_id %>').textContent)"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" width="14" height="14" aria-hidden="true"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg></button>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%# title: heading text; items: array of hashes for _resource_card (name:, url:, dom_id:, subtitle?:, meta?:) %>
|
|
2
|
+
<% items = Array(local_assigns.fetch(:items, [])) %>
|
|
3
|
+
<% if items.any? %>
|
|
4
|
+
<div class="ck-api-endpoint" style="padding-top: 1rem;">
|
|
5
|
+
<p class="ck-kicker" style="margin-bottom: 0.5rem;"><%= local_assigns.fetch(:title) %></p>
|
|
6
|
+
<% items.each do |item| %>
|
|
7
|
+
<%= render "completion_kit/api_reference/resource_card", item %>
|
|
8
|
+
<% end %>
|
|
9
|
+
</div>
|
|
10
|
+
<% end %>
|
|
@@ -20,4 +20,10 @@
|
|
|
20
20
|
base_url: @base_url,
|
|
21
21
|
token: ck_masked_token(@token),
|
|
22
22
|
real_token: @token,
|
|
23
|
-
published_prompts: @published_prompts
|
|
23
|
+
published_prompts: @published_prompts,
|
|
24
|
+
recent_runs: @recent_runs,
|
|
25
|
+
datasets: @datasets,
|
|
26
|
+
metrics: @metrics,
|
|
27
|
+
metric_groups: @metric_groups,
|
|
28
|
+
tags: @tags,
|
|
29
|
+
provider_credentials: @provider_credentials %>
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
<h1 class="ck-title"><%= @dataset.name %></h1>
|
|
9
9
|
</div>
|
|
10
10
|
<div class="ck-actions">
|
|
11
|
+
<%= link_to "Download CSV", dataset_path(@dataset, format: :csv), class: ck_button_classes(:light, variant: :outline) %>
|
|
11
12
|
<%= link_to "Edit", edit_dataset_path(@dataset), class: ck_button_classes(:light, variant: :outline) %>
|
|
12
13
|
</div>
|
|
13
14
|
</section>
|
|
@@ -67,23 +68,6 @@
|
|
|
67
68
|
<% if @runs.any? %>
|
|
68
69
|
<section class="ck-card--spaced">
|
|
69
70
|
<p class="ck-kicker">Runs</p>
|
|
70
|
-
|
|
71
|
-
<table class="ck-results-table ck-runs-table" style="margin-top: 0.5rem;">
|
|
72
|
-
<thead>
|
|
73
|
-
<tr>
|
|
74
|
-
<th>Run</th>
|
|
75
|
-
<th>Responses</th>
|
|
76
|
-
<th>Metrics</th>
|
|
77
|
-
<th>Avg score</th>
|
|
78
|
-
<th>When</th>
|
|
79
|
-
<th></th>
|
|
80
|
-
</tr>
|
|
81
|
-
</thead>
|
|
82
|
-
<tbody>
|
|
83
|
-
<% @runs.each do |run| %>
|
|
84
|
-
<%= render "completion_kit/runs/row", run: run %>
|
|
85
|
-
<% end %>
|
|
86
|
-
</tbody>
|
|
87
|
-
</table>
|
|
71
|
+
<%= render "completion_kit/runs/table", runs: @runs %>
|
|
88
72
|
</section>
|
|
89
73
|
<% end %>
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
<code class="ck-endpoint__url" id="prompt_endpoint"><%= request.base_url %><%= api_v1_prompt_path(@prompt.slug) %></code>
|
|
18
18
|
<button type="button" class="ck-icon-btn" title="Copy endpoint" onclick="navigator.clipboard.writeText(document.getElementById('prompt_endpoint').textContent)"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" width="14" height="14"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg></button>
|
|
19
19
|
</div>
|
|
20
|
+
<% if @prompt.tags.any? %>
|
|
21
|
+
<div class="tag-marks-row">
|
|
22
|
+
<%= render "completion_kit/tags/marks", tags: @prompt.tags %>
|
|
23
|
+
</div>
|
|
24
|
+
<% end %>
|
|
20
25
|
</div>
|
|
21
26
|
|
|
22
27
|
<div class="ck-actions">
|
|
@@ -26,13 +31,7 @@
|
|
|
26
31
|
</div>
|
|
27
32
|
</section>
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
<div class="tag-marks-row tag-marks-row--header">
|
|
31
|
-
<%= render "completion_kit/tags/marks", tags: @prompt.tags %>
|
|
32
|
-
</div>
|
|
33
|
-
<% end %>
|
|
34
|
-
|
|
35
|
-
<section>
|
|
34
|
+
<section class="ck-card--spaced">
|
|
36
35
|
<div class="ck-prompt-preview__header">
|
|
37
36
|
<p class="ck-kicker">Prompt</p>
|
|
38
37
|
<% judged_run = @runs.select { |r| r.prompt_id == @prompt.id && r.status == "completed" }.find { |r| r.responses.joins(:reviews).exists? } %>
|
|
@@ -45,7 +44,7 @@
|
|
|
45
44
|
<p class="ck-hint"><%= link_to "Run a test with judging configured", new_run_path(prompt_id: @prompt.id), class: "ck-link" %> to get AI-suggested improvements.</p>
|
|
46
45
|
<% end %>
|
|
47
46
|
</div>
|
|
48
|
-
<pre class="ck-code ck-code--dark"><%= @prompt.template %></pre>
|
|
47
|
+
<pre class="ck-code ck-code--dark ck-code--prompt"><%= @prompt.template %></pre>
|
|
49
48
|
</section>
|
|
50
49
|
|
|
51
50
|
<% versions = @prompt.family_versions.includes(runs: { responses: :reviews }).to_a %>
|
|
@@ -140,24 +139,7 @@
|
|
|
140
139
|
<% if @runs.any? %>
|
|
141
140
|
<section class="ck-card--spaced">
|
|
142
141
|
<p class="ck-kicker">Runs</p>
|
|
143
|
-
|
|
144
|
-
<table class="ck-results-table ck-runs-table" style="margin-top: 0.5rem;">
|
|
145
|
-
<thead>
|
|
146
|
-
<tr>
|
|
147
|
-
<th>Run</th>
|
|
148
|
-
<th>Responses</th>
|
|
149
|
-
<th>Metrics</th>
|
|
150
|
-
<th>Avg score</th>
|
|
151
|
-
<th>When</th>
|
|
152
|
-
<th></th>
|
|
153
|
-
</tr>
|
|
154
|
-
</thead>
|
|
155
|
-
<tbody>
|
|
156
|
-
<% @runs.each do |run| %>
|
|
157
|
-
<%= render "completion_kit/runs/row", run: run %>
|
|
158
|
-
<% end %>
|
|
159
|
-
</tbody>
|
|
160
|
-
</table>
|
|
142
|
+
<%= render "completion_kit/runs/table", runs: @runs %>
|
|
161
143
|
</section>
|
|
162
144
|
<% end %>
|
|
163
145
|
|
|
@@ -93,10 +93,10 @@
|
|
|
93
93
|
|
|
94
94
|
<div class="ck-field" id="metrics-field">
|
|
95
95
|
<label class="ck-label">Metrics</label>
|
|
96
|
-
<p class="ck-field-hint" id="metrics-hint"></p>
|
|
97
96
|
<% if @all_metrics.empty? %>
|
|
98
97
|
<p class="ck-field-hint" style="color: var(--ck-warning);">No metrics yet. <%= link_to "Create a metric", new_metric_path, class: "ck-link" %></p>
|
|
99
98
|
<% else %>
|
|
99
|
+
<p class="ck-field-hint" id="metrics-hint"></p>
|
|
100
100
|
<% if @metric_groups.any? %>
|
|
101
101
|
<div class="ck-metric-groups">
|
|
102
102
|
<span class="ck-metric-groups__label">Groups</span>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%# Renders a runs table. Locals: runs: enumerable of CompletionKit::Run. %>
|
|
2
|
+
<% runs = local_assigns.fetch(:runs) %>
|
|
3
|
+
<table class="ck-results-table ck-runs-table" style="margin-top: 0.5rem;">
|
|
4
|
+
<thead>
|
|
5
|
+
<tr>
|
|
6
|
+
<th>Run</th>
|
|
7
|
+
<th>Responses</th>
|
|
8
|
+
<th>Metrics</th>
|
|
9
|
+
<th>Avg score</th>
|
|
10
|
+
<th>When</th>
|
|
11
|
+
<th></th>
|
|
12
|
+
</tr>
|
|
13
|
+
</thead>
|
|
14
|
+
<tbody>
|
|
15
|
+
<% runs.each do |run| %>
|
|
16
|
+
<%= render "completion_kit/runs/row", run: run %>
|
|
17
|
+
<% end %>
|
|
18
|
+
</tbody>
|
|
19
|
+
</table>
|
|
@@ -14,23 +14,7 @@
|
|
|
14
14
|
base_path: runs_path %>
|
|
15
15
|
|
|
16
16
|
<% if @runs.any? %>
|
|
17
|
-
|
|
18
|
-
<thead>
|
|
19
|
-
<tr>
|
|
20
|
-
<th>Run</th>
|
|
21
|
-
<th>Responses</th>
|
|
22
|
-
<th>Metrics</th>
|
|
23
|
-
<th>Avg score</th>
|
|
24
|
-
<th>When</th>
|
|
25
|
-
<th></th>
|
|
26
|
-
</tr>
|
|
27
|
-
</thead>
|
|
28
|
-
<tbody>
|
|
29
|
-
<% @runs.each do |run| %>
|
|
30
|
-
<%= render "row", run: run %>
|
|
31
|
-
<% end %>
|
|
32
|
-
</tbody>
|
|
33
|
-
</table>
|
|
17
|
+
<%= render "completion_kit/runs/table", runs: @runs %>
|
|
34
18
|
<% elsif @selected_tags.any? %>
|
|
35
19
|
<div class="ck-empty">
|
|
36
20
|
<p>No runs match these tags. <%= link_to "Clear filters", runs_path, class: "ck-link" %>.</p>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<%= csrf_meta_tags %>
|
|
7
7
|
<%= csp_meta_tag %>
|
|
8
8
|
|
|
9
|
-
<%= favicon_link_tag "completion_kit/
|
|
9
|
+
<%= favicon_link_tag "completion_kit/favicon.ico" %>
|
|
10
10
|
<%= stylesheet_link_tag "completion_kit/application", media: "all" %>
|
|
11
11
|
<%= javascript_include_tag "turbo", type: "module" %>
|
|
12
12
|
<%= javascript_include_tag "completion_kit/application", defer: true %>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<body class="ck-app">
|
|
16
16
|
<header class="ck-topbar">
|
|
17
17
|
<div class="ck-wrap ck-topbar__inner">
|
|
18
|
-
<%= link_to (main_app.respond_to?(:root_path) ? main_app.root_path : prompts_path), class: "ck-brand" do %><%= image_tag "completion_kit/logo.
|
|
18
|
+
<%= link_to (main_app.respond_to?(:root_path) ? main_app.root_path : prompts_path), class: "ck-brand" do %><%= image_tag "completion_kit/logo.png", alt: "CompletionKit", style: "height: 64px; width: auto;" %><span class="ck-brand__name">Completion<span class="ck-brand__kit">Kit</span></span><% end %>
|
|
19
19
|
|
|
20
20
|
<nav class="ck-nav">
|
|
21
21
|
<% active = ->(path) { request.path.start_with?(path) ? ck_button_classes(:dark) : ck_button_classes(:light, variant: :outline) } %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class CreateCompletionKitMcpSessions < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :completion_kit_mcp_sessions do |t|
|
|
4
|
+
t.string :session_id, null: false
|
|
5
|
+
t.datetime :expires_at, null: false
|
|
6
|
+
t.timestamps
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
add_index :completion_kit_mcp_sessions, :session_id, unique: true
|
|
10
|
+
add_index :completion_kit_mcp_sessions, :expires_at
|
|
11
|
+
end
|
|
12
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: completion-kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Damien Bastin
|
|
@@ -227,7 +227,8 @@ files:
|
|
|
227
227
|
- Rakefile
|
|
228
228
|
- app/assets/config/completion_kit_manifest.js
|
|
229
229
|
- app/assets/config/manifest.js
|
|
230
|
-
- app/assets/images/completion_kit/
|
|
230
|
+
- app/assets/images/completion_kit/favicon.ico
|
|
231
|
+
- app/assets/images/completion_kit/logo.png
|
|
231
232
|
- app/assets/javascripts/completion_kit/application.js
|
|
232
233
|
- app/assets/stylesheets/completion_kit/application.css
|
|
233
234
|
- app/controllers/completion_kit/api/v1/base_controller.rb
|
|
@@ -262,6 +263,7 @@ files:
|
|
|
262
263
|
- app/mailers/completion_kit/application_mailer.rb
|
|
263
264
|
- app/models/completion_kit/application_record.rb
|
|
264
265
|
- app/models/completion_kit/dataset.rb
|
|
266
|
+
- app/models/completion_kit/mcp_session.rb
|
|
265
267
|
- app/models/completion_kit/metric.rb
|
|
266
268
|
- app/models/completion_kit/metric_group.rb
|
|
267
269
|
- app/models/completion_kit/metric_group_membership.rb
|
|
@@ -303,6 +305,8 @@ files:
|
|
|
303
305
|
- app/views/completion_kit/api_reference/_authentication.html.erb
|
|
304
306
|
- app/views/completion_kit/api_reference/_body.html.erb
|
|
305
307
|
- app/views/completion_kit/api_reference/_example.html.erb
|
|
308
|
+
- app/views/completion_kit/api_reference/_resource_card.html.erb
|
|
309
|
+
- app/views/completion_kit/api_reference/_resource_list.html.erb
|
|
306
310
|
- app/views/completion_kit/api_reference/index.html.erb
|
|
307
311
|
- app/views/completion_kit/datasets/_form.html.erb
|
|
308
312
|
- app/views/completion_kit/datasets/edit.html.erb
|
|
@@ -339,6 +343,7 @@ files:
|
|
|
339
343
|
- app/views/completion_kit/runs/_sort_toolbar.html.erb
|
|
340
344
|
- app/views/completion_kit/runs/_status_header.html.erb
|
|
341
345
|
- app/views/completion_kit/runs/_status_panel.html.erb
|
|
346
|
+
- app/views/completion_kit/runs/_table.html.erb
|
|
342
347
|
- app/views/completion_kit/runs/edit.html.erb
|
|
343
348
|
- app/views/completion_kit/runs/index.html.erb
|
|
344
349
|
- app/views/completion_kit/runs/new.html.erb
|
|
@@ -375,6 +380,7 @@ files:
|
|
|
375
380
|
- db/migrate/20260507150000_add_temperature_ignored_to_runs.rb
|
|
376
381
|
- db/migrate/20260509000001_create_completion_kit_tags.rb
|
|
377
382
|
- db/migrate/20260509000002_create_completion_kit_taggings.rb
|
|
383
|
+
- db/migrate/20260513000001_create_completion_kit_mcp_sessions.rb
|
|
378
384
|
- lib/completion-kit.rb
|
|
379
385
|
- lib/completion_kit.rb
|
|
380
386
|
- lib/completion_kit/concurrency_check.rb
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="2.4 2.3 88.74 89.1" width="512" height="512">
|
|
2
|
-
<path d="M57.7,56.4c1.2-1.2,1.8-3.1,1.4-5c-0.5-2-2.1-3.6-4.1-4c-3.5-0.7-6.6,1.9-6.6,5.3c0,1.5,0.6,2.8,1.5,3.8H37.4l0-12.6c-1.2,1.2-3.1,1.8-5,1.4c-2-0.5-3.6-2.1-4-4.1c-0.7-3.5,1.9-6.6,5.3-6.6c1.5,0,2.8,0.6,3.8,1.5V23.5H10.1c-3.1,0-5.7,2.5-5.7,5.7v54.5c0,3.1,2.5,5.7,5.7,5.7h27.2h27.2c3.1,0,5.7-2.5,5.7-5.7V56.4H57.7z" fill="#0891B2"/>
|
|
3
|
-
<path d="M54.7,37.5c-0.1-1.7,0.6-3.5,2.2-4.8c1.6-1.2,3.9-1.4,5.7-0.5c3.2,1.7,3.8,5.7,1.6,8.3c-0.9,1.1-2.3,1.8-3.6,1.9l9.6,8.1l17.5-20.9c2-2.4,1.7-6-0.7-8L66.2,4.3l-8.1,9.6c-0.2-1.7-1.2-3.4-3-4.3c-1.8-0.9-4.1-0.7-5.7,0.5c-2.8,2.2-2.8,6.3-0.2,8.4c1.1,0.9,2.5,1.4,3.9,1.2L45,29.5L54.7,37.5z" fill="#06B6D4"/>
|
|
4
|
-
<path d="M70.2,52.6c-0.5,0-0.9-0.2-1.3-0.5L59.3,44c-0.6-0.5-0.9-1.4-0.6-2.1c0.2-0.8,0.9-1.3,1.7-1.4c0.9-0.1,1.7-0.5,2.3-1.2c0.7-0.8,0.9-1.8,0.7-2.8c-0.2-1-0.8-1.9-1.8-2.4c-1.1-0.6-2.6-0.5-3.6,0.3c-1,0.7-1.5,1.8-1.4,3c0.1,0.8-0.4,1.6-1.1,1.9c-0.7,0.4-1.6,0.3-2.2-0.2L43.8,31c-0.4-0.3-0.7-0.8-0.7-1.4c0-0.5,0.1-1.1,0.5-1.5l5.9-7.1c-0.5-0.3-1-0.6-1.5-1c-1.7-1.4-2.7-3.6-2.6-5.8c0-2.2,1.1-4.3,2.9-5.7c2.2-1.8,5.3-2.1,7.9-0.7c1.1,0.6,2,1.3,2.6,2.3l6-7.1C65,2.6,65.5,2.3,66,2.3c0.5,0,1.1,0.1,1.5,0.5l20.9,17.5c1.6,1.3,2.5,3.2,2.7,5.2s-0.4,4-1.8,5.6L71.8,51.9c-0.3,0.4-0.8,0.7-1.4,0.7C70.4,52.6,70.3,52.6,70.2,52.6z M64.4,43.1l5.6,4.7l16.2-19.3c0.6-0.8,0.9-1.7,0.8-2.7s-0.5-1.9-1.3-2.5L66.4,7.1l-6.8,8.1c-0.5,0.6-1.4,0.9-2.1,0.6c-0.8-0.2-1.3-0.9-1.4-1.7c-0.1-1.2-0.8-2.2-1.9-2.7c-1.1-0.6-2.6-0.4-3.6,0.3c-0.9,0.7-1.3,1.6-1.4,2.7c0,1,0.4,2,1.2,2.7c0.7,0.6,1.5,0.9,2.4,0.8c0.8-0.1,1.6,0.4,1.9,1.1s0.3,1.6-0.2,2.2l-6.8,8.1l5.6,4.7c0.5-1,1.3-2,2.2-2.7c2.3-1.7,5.4-2,7.9-0.6c2,1.1,3.4,3,3.8,5.2s-0.2,4.5-1.6,6.2C65.4,42.4,64.9,42.8,64.4,43.1z" fill="#475569"/>
|
|
5
|
-
<path d="M70.3,54.4H61c0.3-1.1,0.3-2.3,0-3.5c-0.6-2.8-2.8-5-5.6-5.5c-2.2-0.5-4.5,0.1-6.3,1.5c-1.7,1.4-2.7,3.5-2.7,5.8c0,0.6,0.1,1.2,0.2,1.8h-7.3V43.8c0-0.8-0.5-1.5-1.2-1.8c-0.7-0.3-1.6-0.1-2.2,0.4c-0.8,0.8-2,1.2-3.2,0.9c-1.2-0.3-2.2-1.3-2.5-2.5c-0.2-1.1,0-2.1,0.7-2.9c0.7-0.8,1.6-1.3,2.6-1.3c0.9,0,1.7,0.3,2.4,1c0.6,0.6,1.4,0.7,2.2,0.4c0.7-0.3,1.2-1,1.2-1.8V23.5c0-1.1-0.9-2-2-2H10.1c-4.2,0-7.7,3.4-7.7,7.7v54.5c0,4.2,3.4,7.7,7.7,7.7h54.5c4.2,0,7.7-3.4,7.7-7.7V56.4C72.3,55.3,71.4,54.4,70.3,54.4z M10.1,25.5h25.2v7.3c-0.6-0.1-1.2-0.2-1.8-0.2c-2.2,0-4.3,1-5.8,2.7s-2,4-1.5,6.3c0.6,2.8,2.8,5,5.5,5.6c1.2,0.3,2.4,0.2,3.5,0v7.3H24.8c-0.8,0-1.5,0.5-1.8,1.2c-0.3,0.7-0.1,1.6,0.4,2.2c0.8,0.8,1.1,2,0.9,3.2c-0.3,1.2-1.3,2.2-2.6,2.5c-1.1,0.2-2.1,0-2.9-0.7c-0.8-0.7-1.3-1.6-1.3-2.6c0-0.9,0.3-1.7,1-2.4c0.6-0.6,0.7-1.4,0.4-2.2c-0.3-0.7-1-1.2-1.8-1.2H6.5V29.2C6.5,27.1,8.1,25.5,10.1,25.5z M36.6,74.9c-0.7,0.3-1.2,1-1.2,1.8v10.6H10.1c-2,0-3.7-1.6-3.7-3.7V58.4h7.3c-0.1,0.6-0.2,1.2-0.2,1.8c0,2.2,1,4.3,2.7,5.8c1.7,1.4,4,2,6.3,1.5c2.8-0.6,5-2.8,5.6-5.5c0.3-1.2,0.2-2.4,0-3.5h7.3V69c0,0.8,0.5,1.5,1.2,1.8c0.7,0.3,1.6,0.1,2.2-0.4c0.8-0.8,2-1.1,3.2-0.9c1.2,0.3,2.2,1.3,2.5,2.6c0.2,1.1,0,2.1-0.7,2.9c-0.7,0.8-1.6,1.3-2.6,1.3c-0.9,0-1.7-0.3-2.4-1C38.2,74.7,37.3,74.6,36.6,74.9z M68.3,83.6c0,2-1.6,3.7-3.7,3.7H39.4V80c0.6,0.1,1.2,0.2,1.8,0.2c2.2,0,4.3-1,5.8-2.7c1.4-1.7,2-4,1.5-6.3c-0.6-2.8-2.8-5-5.5-5.6c-1.2-0.3-2.4-0.2-3.5,0v-7.3H50c0.8,0,1.5-0.5,1.8-1.2c0.3-0.7,0.1-1.6-0.4-2.2c-0.6-0.6-1-1.5-1-2.4c0-1,0.5-2,1.3-2.6c0.8-0.7,1.8-0.9,2.9-0.7c1.2,0.3,2.3,1.3,2.6,2.5c0.3,1.2-0.1,2.4-0.9,3.2c-0.6,0.6-0.7,1.4-0.4,2.2c0.3,0.7,1,1.2,1.8,1.2h10.6V83.6z" fill="#475569"/>
|
|
6
|
-
</svg>
|