livepilot 1.23.0 → 1.23.1
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.
- package/CHANGELOG.md +8 -0
- package/mcp_server/__init__.py +1 -1
- package/mcp_server/atlas/overlays.py +39 -18
- package/package.json +1 -1
- package/remote_script/LivePilot/__init__.py +1 -1
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.23.1 — 2026-04-25
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- `extension_atlas_search` multi-word queries returned 0 hits because the search did literal-substring matching. `"sophie ponyboy"` failed to match `"SOPHIE — Ponyboy kick"` because of the em-dash separator. Now: query is tokenized on whitespace, each token must match somewhere (AND semantics), per-token scores sum for ranking. Single-token queries collapse to the original behavior — fully backwards-compatible.
|
|
7
|
+
|
|
8
|
+
### Tests
|
|
9
|
+
- 3 new tests covering multi-token AND, em-dash separator handling, per-token score aggregation.
|
|
10
|
+
|
|
3
11
|
## v1.23.0 — 2026-04-25
|
|
4
12
|
|
|
5
13
|
### Added
|
package/mcp_server/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""LivePilot MCP Server — bridges MCP protocol to Ableton Live."""
|
|
2
|
-
__version__ = "1.23.
|
|
2
|
+
__version__ = "1.23.1"
|
|
@@ -91,14 +91,20 @@ class OverlayIndex:
|
|
|
91
91
|
def search(self, query: str, namespace: Optional[str] = None,
|
|
92
92
|
entity_type: Optional[str] = None,
|
|
93
93
|
limit: int = 10) -> list[OverlayEntry]:
|
|
94
|
-
"""Weighted substring search.
|
|
94
|
+
"""Weighted substring search with whitespace-tokenized AND semantics.
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
The query is split on whitespace into tokens. Each token is scored
|
|
97
|
+
against each entry independently:
|
|
98
|
+
+1000 if token == entity_id (case-insensitive exact match)
|
|
98
99
|
+100 per substring hit in name
|
|
99
100
|
+50 per substring hit in tag or artist
|
|
100
101
|
+10 per substring hit in description
|
|
101
102
|
|
|
103
|
+
An entry matches only if EVERY token scores > 0 somewhere (AND
|
|
104
|
+
semantics — prevents 'sophie ponyboy' from matching unrelated
|
|
105
|
+
entries that contain only one of the two words). The entry's
|
|
106
|
+
final score is the sum across all tokens.
|
|
107
|
+
|
|
102
108
|
Sorts by descending score, then by entity_id for stable ties.
|
|
103
109
|
Filters by namespace and/or entity_type if provided.
|
|
104
110
|
Empty query returns empty list.
|
|
@@ -106,6 +112,9 @@ class OverlayIndex:
|
|
|
106
112
|
q = (query or "").strip().lower()
|
|
107
113
|
if not q:
|
|
108
114
|
return []
|
|
115
|
+
tokens = q.split()
|
|
116
|
+
if not tokens:
|
|
117
|
+
return []
|
|
109
118
|
|
|
110
119
|
scored: list[tuple[int, str, OverlayEntry]] = []
|
|
111
120
|
for entry in self._entries.values():
|
|
@@ -113,21 +122,33 @@ class OverlayIndex:
|
|
|
113
122
|
continue
|
|
114
123
|
if entity_type is not None and entry.entity_type != entity_type:
|
|
115
124
|
continue
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
for
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
for
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
125
|
+
|
|
126
|
+
name_lower = entry.name.lower()
|
|
127
|
+
entity_id_lower = entry.entity_id.lower()
|
|
128
|
+
description_lower = entry.description.lower()
|
|
129
|
+
tags_lower = [str(t).lower() for t in entry.tags]
|
|
130
|
+
artists_lower = [str(a).lower() for a in entry.artists]
|
|
131
|
+
|
|
132
|
+
token_scores = []
|
|
133
|
+
for tok in tokens:
|
|
134
|
+
s = 0
|
|
135
|
+
if entity_id_lower == tok:
|
|
136
|
+
s += 1000
|
|
137
|
+
if tok in name_lower:
|
|
138
|
+
s += 100
|
|
139
|
+
for tag in tags_lower:
|
|
140
|
+
if tok in tag:
|
|
141
|
+
s += 50
|
|
142
|
+
for artist in artists_lower:
|
|
143
|
+
if tok in artist:
|
|
144
|
+
s += 50
|
|
145
|
+
if tok in description_lower:
|
|
146
|
+
s += 10
|
|
147
|
+
token_scores.append(s)
|
|
148
|
+
|
|
149
|
+
# AND semantics — every token must match somewhere
|
|
150
|
+
if all(s > 0 for s in token_scores):
|
|
151
|
+
scored.append((sum(token_scores), entry.entity_id, entry))
|
|
131
152
|
|
|
132
153
|
scored.sort(key=lambda triple: (-triple[0], triple[1]))
|
|
133
154
|
return [entry for (_, _, entry) in scored[:max(0, limit)]]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livepilot",
|
|
3
|
-
"version": "1.23.
|
|
3
|
+
"version": "1.23.1",
|
|
4
4
|
"mcpName": "io.github.dreamrec/livepilot",
|
|
5
5
|
"description": "Agentic production system for Ableton Live 12 — 433 tools, 53 domains, 44 semantic moves. Device atlas (5264 devices, 120 enriched, 7 indexes), Splice intelligence (gRPC + GraphQL describe-a-sound + preview + collections + presets), 9-band spectral perception auto-loaded via ensure_analyzer_on_master, Creative Director skill, technique memory, 12 creative intelligence engines",
|
|
6
6
|
"author": "Pilot Studio",
|
|
@@ -5,7 +5,7 @@ Entry point for the ControlSurface. Ableton calls create_instance(c_instance)
|
|
|
5
5
|
when this script is selected in Preferences > Link, Tempo & MIDI.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
__version__ = "1.23.
|
|
8
|
+
__version__ = "1.23.1"
|
|
9
9
|
|
|
10
10
|
from _Framework.ControlSurface import ControlSurface
|
|
11
11
|
from . import router
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/dreamrec/LivePilot",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "1.23.
|
|
9
|
+
"version": "1.23.1",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "livepilot",
|
|
14
|
-
"version": "1.23.
|
|
14
|
+
"version": "1.23.1",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
}
|