agentify-toolkit 0.4.3a1__py3-none-any.whl

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.
agentify/specs.py ADDED
@@ -0,0 +1,19 @@
1
+ # Copyright 2026 Backplane Software
2
+ # Licensed under the Apache License, Version 2.0
3
+
4
+ from pathlib import Path
5
+ import yaml
6
+
7
+ def load_agent_specs(agent_dir: Path | str = "agents") -> list[dict]:
8
+ agent_dir = Path(agent_dir)
9
+ specs = []
10
+ for path in agent_dir.glob("*.yaml"):
11
+ with open(path, "r") as f:
12
+ spec = yaml.safe_load(f)
13
+ spec["_file"] = path.name # optional metadata
14
+ specs.append(spec)
15
+ return specs
16
+
17
+
18
+
19
+
@@ -0,0 +1,230 @@
1
+ body {
2
+ font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
3
+ background: #f1f5f9;
4
+ margin: 0;
5
+ display: flex;
6
+ justify-content: center;
7
+ align-items: center;
8
+ height: 100vh;
9
+ padding: 2rem;
10
+ box-sizing: border-box;
11
+ overflow: hidden;
12
+ }
13
+
14
+ #chat-container {
15
+ width: 100%;
16
+ max-width: 600px;
17
+ background: #ffffff;
18
+ border-radius: 24px;
19
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
20
+ display: flex;
21
+ flex-direction: column;
22
+ padding: 2rem;
23
+ height: 600px;
24
+ border: 1px solid rgba(226, 232, 240, 0.8);
25
+ }
26
+
27
+ #chat-header {
28
+ font-family: "Poppins", sans-serif;
29
+ font-size: 1.75rem;
30
+ font-weight: 700;
31
+ margin-bottom: 0.25rem;
32
+ color: #1e293b;
33
+ }
34
+
35
+ .tagline {
36
+ font-family: "Poppins", sans-serif;
37
+ font-size: 0.875rem;
38
+ color: #64748b;
39
+ margin-bottom: 1.5rem;
40
+ font-weight: 400;
41
+ }
42
+
43
+ #chat {
44
+ flex-grow: 1;
45
+ overflow-y: auto;
46
+ margin-bottom: 1.5rem;
47
+ padding-right: 0.5rem;
48
+ display: flex;
49
+ flex-direction: column;
50
+ }
51
+
52
+ /* Custom scrollbar */
53
+ #chat::-webkit-scrollbar {
54
+ width: 6px;
55
+ }
56
+
57
+ #chat::-webkit-scrollbar-track {
58
+ background: #f1f5f9;
59
+ border-radius: 10px;
60
+ }
61
+
62
+ #chat::-webkit-scrollbar-thumb {
63
+ background: #cbd5e1;
64
+ border-radius: 10px;
65
+ transition: background 0.2s;
66
+ }
67
+
68
+ #chat::-webkit-scrollbar-thumb:hover {
69
+ background: #94a3b8;
70
+ }
71
+
72
+ .message {
73
+ padding: 0.875rem 1.125rem;
74
+ margin-bottom: 0.75rem;
75
+ border-radius: 18px;
76
+ position: relative;
77
+ max-width: 75%;
78
+ word-wrap: break-word;
79
+ line-height: 1.5;
80
+ animation: fadeIn 0.3s ease-in;
81
+ }
82
+
83
+ @keyframes fadeIn {
84
+ from {
85
+ opacity: 0;
86
+ transform: translateY(10px);
87
+ }
88
+ to {
89
+ opacity: 1;
90
+ transform: translateY(0);
91
+ }
92
+ }
93
+
94
+ .message.user {
95
+ background: #1e293b;
96
+ color: #ffffff;
97
+ align-self: flex-end;
98
+ border-bottom-right-radius: 6px;
99
+ box-shadow: 0 2px 8px rgba(30, 41, 59, 0.15);
100
+ margin-left: auto;
101
+ }
102
+
103
+ .message.agent {
104
+ background: #f8fafc;
105
+ color: #1e293b;
106
+ align-self: flex-start;
107
+ border-bottom-left-radius: 6px;
108
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
109
+ border: 1px solid #e2e8f0;
110
+ }
111
+
112
+ .message.agent.typing {
113
+ font-style: italic;
114
+ color: #64748b;
115
+ background: #f8fafc;
116
+ border: 1px solid #e2e8f0;
117
+ }
118
+
119
+ .timestamp {
120
+ font-size: 0.7rem;
121
+ color: rgba(255, 255, 255, 0.7);
122
+ margin-top: 4px;
123
+ display: block;
124
+ }
125
+
126
+ .message.agent .timestamp {
127
+ color: #94a3b8;
128
+ }
129
+
130
+ form {
131
+ display: flex;
132
+ gap: 0.75rem;
133
+ align-items: center;
134
+ }
135
+
136
+ #question-input {
137
+ flex-grow: 1;
138
+ padding: 0.875rem 1.25rem;
139
+ font-size: 0.95rem;
140
+ border-radius: 16px;
141
+ border: 1.5px solid #e2e8f0;
142
+ outline: none;
143
+ background-color: #ffffff;
144
+ transition: all 0.2s ease-in-out;
145
+ font-family: inherit;
146
+ }
147
+
148
+ #question-input:focus {
149
+ border-color: #1e293b;
150
+ box-shadow: 0 0 0 3px rgba(30, 41, 59, 0.08);
151
+ background-color: #ffffff;
152
+ }
153
+
154
+ #question-input::placeholder {
155
+ color: #94a3b8;
156
+ }
157
+
158
+ button {
159
+ background: #1e293b;
160
+ color: white;
161
+ border: none;
162
+ border-radius: 16px;
163
+ padding: 0.875rem 1.5rem;
164
+ font-size: 0.95rem;
165
+ font-weight: 600;
166
+ cursor: pointer;
167
+ transition: all 0.2s ease-in-out;
168
+ box-shadow: 0 2px 8px rgba(30, 41, 59, 0.15);
169
+ white-space: nowrap;
170
+ }
171
+
172
+ button:hover {
173
+ background: #0f172a;
174
+ transform: translateY(-1px);
175
+ box-shadow: 0 4px 12px rgba(30, 41, 59, 0.2);
176
+ }
177
+
178
+ button:active {
179
+ transform: translateY(0);
180
+ box-shadow: 0 1px 4px rgba(30, 41, 59, 0.15);
181
+ }
182
+
183
+ button:disabled {
184
+ opacity: 0.6;
185
+ cursor: not-allowed;
186
+ transform: none;
187
+ }
188
+
189
+ button:focus {
190
+ outline: 2px solid #64748b;
191
+ outline-offset: 2px;
192
+ }
193
+
194
+ /* Optional: Add a subtle pattern to the body */
195
+ body::before {
196
+ content: "";
197
+ position: fixed;
198
+ top: 0;
199
+ left: 0;
200
+ width: 100%;
201
+ height: 100%;
202
+ background-image: radial-gradient(
203
+ circle at 20% 30%,
204
+ rgba(148, 163, 184, 0.03) 0%,
205
+ transparent 50%
206
+ ),
207
+ radial-gradient(
208
+ circle at 80% 70%,
209
+ rgba(148, 163, 184, 0.03) 0%,
210
+ transparent 50%
211
+ );
212
+ pointer-events: none;
213
+ z-index: -1;
214
+ }
215
+
216
+ /* Responsive adjustments */
217
+ @media (max-width: 640px) {
218
+ body {
219
+ padding: 1rem;
220
+ }
221
+
222
+ #chat-container {
223
+ padding: 1.5rem;
224
+ height: calc(100vh - 2rem);
225
+ }
226
+
227
+ .message {
228
+ max-width: 85%;
229
+ }
230
+ }
agentify/ui/chat.css ADDED
@@ -0,0 +1,230 @@
1
+ body {
2
+ font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
3
+ background: #f1f5f9;
4
+ margin: 0;
5
+ display: flex;
6
+ justify-content: center;
7
+ align-items: center;
8
+ height: 100vh;
9
+ padding: 2rem;
10
+ box-sizing: border-box;
11
+ overflow: hidden;
12
+ }
13
+
14
+ #chat-container {
15
+ width: 100%;
16
+ max-width: 600px;
17
+ background: #ffffff;
18
+ border-radius: 24px;
19
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
20
+ display: flex;
21
+ flex-direction: column;
22
+ padding: 2rem;
23
+ height: 600px;
24
+ border: 1px solid rgba(226, 232, 240, 0.8);
25
+ }
26
+
27
+ #chat-header {
28
+ font-family: "Poppins", sans-serif;
29
+ font-size: 1.75rem;
30
+ font-weight: 700;
31
+ margin-bottom: 0.25rem;
32
+ color: #1e293b;
33
+ }
34
+
35
+ .tagline {
36
+ font-family: "Poppins", sans-serif;
37
+ font-size: 0.875rem;
38
+ color: #64748b;
39
+ margin-bottom: 1.5rem;
40
+ font-weight: 400;
41
+ }
42
+
43
+ #chat {
44
+ flex-grow: 1;
45
+ overflow-y: auto;
46
+ margin-bottom: 1.5rem;
47
+ padding-right: 0.5rem;
48
+ display: flex;
49
+ flex-direction: column;
50
+ }
51
+
52
+ /* Custom scrollbar */
53
+ #chat::-webkit-scrollbar {
54
+ width: 6px;
55
+ }
56
+
57
+ #chat::-webkit-scrollbar-track {
58
+ background: #f1f5f9;
59
+ border-radius: 10px;
60
+ }
61
+
62
+ #chat::-webkit-scrollbar-thumb {
63
+ background: #cbd5e1;
64
+ border-radius: 10px;
65
+ transition: background 0.2s;
66
+ }
67
+
68
+ #chat::-webkit-scrollbar-thumb:hover {
69
+ background: #94a3b8;
70
+ }
71
+
72
+ .message {
73
+ padding: 0.875rem 1.125rem;
74
+ margin-bottom: 0.75rem;
75
+ border-radius: 18px;
76
+ position: relative;
77
+ max-width: 75%;
78
+ word-wrap: break-word;
79
+ line-height: 1.5;
80
+ animation: fadeIn 0.3s ease-in;
81
+ }
82
+
83
+ @keyframes fadeIn {
84
+ from {
85
+ opacity: 0;
86
+ transform: translateY(10px);
87
+ }
88
+ to {
89
+ opacity: 1;
90
+ transform: translateY(0);
91
+ }
92
+ }
93
+
94
+ .message.user {
95
+ background: #1e293b;
96
+ color: #ffffff;
97
+ align-self: flex-end;
98
+ border-bottom-right-radius: 6px;
99
+ box-shadow: 0 2px 8px rgba(30, 41, 59, 0.15);
100
+ margin-left: auto;
101
+ }
102
+
103
+ .message.agent {
104
+ background: #f8fafc;
105
+ color: #1e293b;
106
+ align-self: flex-start;
107
+ border-bottom-left-radius: 6px;
108
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
109
+ border: 1px solid #e2e8f0;
110
+ }
111
+
112
+ .message.agent.typing {
113
+ font-style: italic;
114
+ color: #64748b;
115
+ background: #f8fafc;
116
+ border: 1px solid #e2e8f0;
117
+ }
118
+
119
+ .timestamp {
120
+ font-size: 0.7rem;
121
+ color: rgba(255, 255, 255, 0.7);
122
+ margin-top: 4px;
123
+ display: block;
124
+ }
125
+
126
+ .message.agent .timestamp {
127
+ color: #94a3b8;
128
+ }
129
+
130
+ form {
131
+ display: flex;
132
+ gap: 0.75rem;
133
+ align-items: center;
134
+ }
135
+
136
+ #question-input {
137
+ flex-grow: 1;
138
+ padding: 0.875rem 1.25rem;
139
+ font-size: 0.95rem;
140
+ border-radius: 16px;
141
+ border: 1.5px solid #e2e8f0;
142
+ outline: none;
143
+ background-color: #ffffff;
144
+ transition: all 0.2s ease-in-out;
145
+ font-family: inherit;
146
+ }
147
+
148
+ #question-input:focus {
149
+ border-color: #1e293b;
150
+ box-shadow: 0 0 0 3px rgba(30, 41, 59, 0.08);
151
+ background-color: #ffffff;
152
+ }
153
+
154
+ #question-input::placeholder {
155
+ color: #94a3b8;
156
+ }
157
+
158
+ button {
159
+ background: #1e293b;
160
+ color: white;
161
+ border: none;
162
+ border-radius: 16px;
163
+ padding: 0.875rem 1.5rem;
164
+ font-size: 0.95rem;
165
+ font-weight: 600;
166
+ cursor: pointer;
167
+ transition: all 0.2s ease-in-out;
168
+ box-shadow: 0 2px 8px rgba(30, 41, 59, 0.15);
169
+ white-space: nowrap;
170
+ }
171
+
172
+ button:hover {
173
+ background: #0f172a;
174
+ transform: translateY(-1px);
175
+ box-shadow: 0 4px 12px rgba(30, 41, 59, 0.2);
176
+ }
177
+
178
+ button:active {
179
+ transform: translateY(0);
180
+ box-shadow: 0 1px 4px rgba(30, 41, 59, 0.15);
181
+ }
182
+
183
+ button:disabled {
184
+ opacity: 0.6;
185
+ cursor: not-allowed;
186
+ transform: none;
187
+ }
188
+
189
+ button:focus {
190
+ outline: 2px solid #64748b;
191
+ outline-offset: 2px;
192
+ }
193
+
194
+ /* Optional: Add a subtle pattern to the body */
195
+ body::before {
196
+ content: "";
197
+ position: fixed;
198
+ top: 0;
199
+ left: 0;
200
+ width: 100%;
201
+ height: 100%;
202
+ background-image: radial-gradient(
203
+ circle at 20% 30%,
204
+ rgba(148, 163, 184, 0.03) 0%,
205
+ transparent 50%
206
+ ),
207
+ radial-gradient(
208
+ circle at 80% 70%,
209
+ rgba(148, 163, 184, 0.03) 0%,
210
+ transparent 50%
211
+ );
212
+ pointer-events: none;
213
+ z-index: -1;
214
+ }
215
+
216
+ /* Responsive adjustments */
217
+ @media (max-width: 640px) {
218
+ body {
219
+ padding: 1rem;
220
+ }
221
+
222
+ #chat-container {
223
+ padding: 1.5rem;
224
+ height: calc(100vh - 2rem);
225
+ }
226
+
227
+ .message {
228
+ max-width: 85%;
229
+ }
230
+ }
agentify/ui/chat.html ADDED
@@ -0,0 +1,107 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Agentify Web UI</title>
5
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
6
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
7
+ <link
8
+ href="https://fonts.googleapis.com/css2?family=Poppins:wght@500;600;700&display=swap"
9
+ rel="stylesheet"
10
+ />
11
+
12
+ <script src="ui/htmx.min.js"></script>
13
+ <link rel="stylesheet" href="ui/chat.css" />
14
+ </head>
15
+ <body>
16
+ <div id="chat-container">
17
+ <h2 id="chat-header">{{AGENT_NAME}}</h2>
18
+ <div class="tagline">
19
+ Model: <strong>{{AGENT_MODEL_ID}}</strong> Provider:
20
+ <strong>{{AGENT_PROVIDER}}</strong>
21
+ </div>
22
+ <div id="chat"></div>
23
+ <form
24
+ id="chat-form"
25
+ action="/ask"
26
+ hx-post="/ask"
27
+ hx-target="#chat"
28
+ hx-swap="beforeend"
29
+ method="post"
30
+ >
31
+ <input
32
+ id="question-input"
33
+ type="text"
34
+ name="question"
35
+ placeholder="Ask your agent..."
36
+ required
37
+ autocomplete="off"
38
+ />
39
+ <button type="submit">Send</button>
40
+ </form>
41
+ </div>
42
+
43
+ <script>
44
+ const form = document.getElementById("chat-form");
45
+ const input = document.getElementById("question-input");
46
+ const chat = document.getElementById("chat");
47
+
48
+ // Format timestamp as HH:MM
49
+ function getTimestamp() {
50
+ const now = new Date();
51
+ return (
52
+ now.getHours().toString().padStart(2, "0") +
53
+ ":" +
54
+ now.getMinutes().toString().padStart(2, "0")
55
+ );
56
+ }
57
+
58
+ // Before HTMX request: append user message, clear input, add agent typing bubble
59
+ form.addEventListener("htmx:beforeRequest", (evt) => {
60
+ const userMessage = input.value.trim();
61
+ if (!userMessage) return;
62
+
63
+ // Add user message immediately
64
+ const userBubble = document.createElement("div");
65
+ userBubble.className = "message user";
66
+ userBubble.textContent = userMessage;
67
+
68
+ // Add timestamp
69
+ const span = document.createElement("div");
70
+ span.className = "timestamp";
71
+ span.textContent = getTimestamp();
72
+ userBubble.appendChild(span);
73
+
74
+ chat.appendChild(userBubble);
75
+
76
+ // Clear input
77
+ input.value = "";
78
+
79
+ // Add agent typing bubble
80
+ const typingBubble = document.createElement("div");
81
+ typingBubble.className = "message agent typing";
82
+ typingBubble.textContent = "Agent is thinking...";
83
+ chat.appendChild(typingBubble);
84
+
85
+ chat.scrollTop = chat.scrollHeight;
86
+ });
87
+
88
+ // After HTMX swaps new content from server
89
+ document.body.addEventListener("htmx:afterSwap", (evt) => {
90
+ // Remove typing bubble
91
+ const typing = chat.querySelector(".message.agent.typing");
92
+ if (typing) typing.remove();
93
+
94
+ // Add timestamp to last message (agent)
95
+ const lastMessage = chat.lastElementChild;
96
+ if (lastMessage && !lastMessage.querySelector(".timestamp")) {
97
+ const span = document.createElement("div");
98
+ span.className = "timestamp";
99
+ span.textContent = getTimestamp();
100
+ lastMessage.appendChild(span);
101
+ }
102
+
103
+ chat.scrollTop = chat.scrollHeight;
104
+ });
105
+ </script>
106
+ </body>
107
+ </html>