Live Contracts canvas: Chicago permits × workers_500k × playbook patterns
New devop.live/lakehouse section pairs live public Chicago building
permits with derived staffing contracts, ranked candidates from the
500K worker bench, and meta-index discovered patterns per role+geo.
Makes the Phase 19 boost + Path 2 pattern discovery visible on real
external data, without needing a paying client to demo.
Backend:
- New /intelligence/permit_contracts endpoint
- Fetches 6 recent Chicago permits > $250K from the Socrata API
- Derives proposed fill: 1 worker per $150K of permit value (capped 2-8)
- For each: /vectors/hybrid with use_playbook_memory=true,
playbook_memory_k=25, auto availability>0.5 filter
- For each: /vectors/playbook_memory/patterns with k=25 min_freq=0.3
- Returns permit + proposed contract + top 5 candidates with boosts
and citations + discovered pattern + pattern_matched count
Frontend:
- New "Live Contracts" section on search.html between today's sim
contracts and Market Intelligence
- Per-permit card: cost + work_type + address + proposed role/count
+ pool size + top 3 candidates (with endorsement chip when boost
fires) + memory-derived pattern ("MEMORY (N playbooks): recurring
certifications: OSHA-10 47%, Forklift... · archetype mostly: ...")
Real working demo even without paying clients: shows the system
operating on genuinely external data with our synthetic-data-derived
learning applied.
This commit is contained in:
parent
f8e8d25b5f
commit
5c39c74fe4
@ -1240,6 +1240,107 @@ tr:hover{background:#111827}
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intelligence: Chicago permits → assumed staffing contracts with
|
||||||
|
// Phase 19-ranked candidates and Path-2 discovered patterns. Each
|
||||||
|
// card pairs a REAL permit (live from data.cityofchicago.org) with
|
||||||
|
// a PROPOSED fill drawn from our 500K worker bench. Surfaces the
|
||||||
|
// meta-index dimension directly: "what past similar fills had in
|
||||||
|
// common" for this role + geo.
|
||||||
|
if (url.pathname === "/intelligence/permit_contracts" && req.method === "POST") {
|
||||||
|
const start = Date.now();
|
||||||
|
try {
|
||||||
|
const permitUrl = "https://data.cityofchicago.org/resource/ydr8-5enu.json";
|
||||||
|
// Recent + substantial permits only — skip tiny ones that
|
||||||
|
// don't imply real staffing demand.
|
||||||
|
const permits: any[] = await fetch(
|
||||||
|
`${permitUrl}?$select=permit_type,work_type,work_description,reported_cost,street_number,street_direction,street_name,community_area,issue_date&`
|
||||||
|
+ `$where=reported_cost>250000 AND issue_date>'2025-06-01'`
|
||||||
|
+ `&$order=issue_date DESC&$limit=6`
|
||||||
|
).then(r => r.json()).catch(() => []);
|
||||||
|
|
||||||
|
const typeToRole: Record<string, string> = {
|
||||||
|
"Electrical Work": "Electrician",
|
||||||
|
"Masonry Work": "Production Worker",
|
||||||
|
"Mechanical Work": "Maintenance Tech",
|
||||||
|
"Reroofing": "Production Worker",
|
||||||
|
"Plumbing Work": "Maintenance Tech",
|
||||||
|
};
|
||||||
|
|
||||||
|
const contracts: any[] = [];
|
||||||
|
for (const p of permits) {
|
||||||
|
const cost = parseFloat(p.reported_cost || 0);
|
||||||
|
// Industry heuristic — one worker per $150K of permit value,
|
||||||
|
// capped at 8 per contract for staffing realism.
|
||||||
|
const count = Math.min(Math.max(Math.round(cost / 150000), 2), 8);
|
||||||
|
const role = typeToRole[p.work_type || ""] || "Production Worker";
|
||||||
|
const city = "Chicago";
|
||||||
|
const state = "IL";
|
||||||
|
|
||||||
|
// Phase 19 ranked candidates. Soft availability filter
|
||||||
|
// auto-applied by /search — this mirrors the real recruiter
|
||||||
|
// query path exactly.
|
||||||
|
const searchRes = await api("POST", "/vectors/hybrid", {
|
||||||
|
index_name: "workers_500k_v1",
|
||||||
|
filter_dataset: "workers_500k",
|
||||||
|
id_column: "worker_id",
|
||||||
|
sql_filter: `role = '${role}' AND state = '${state}' AND city = '${city}' AND CAST(availability AS DOUBLE) > 0.5`,
|
||||||
|
question: `${role} for ${p.work_type || "construction"} in ${city}`,
|
||||||
|
top_k: 5, generate: false,
|
||||||
|
use_playbook_memory: true, playbook_memory_k: 25,
|
||||||
|
}).catch(() => ({ sources: [] as any[] }));
|
||||||
|
|
||||||
|
// Path 2 — discovered patterns for this role in this city.
|
||||||
|
const patternRes = await api("POST", "/vectors/playbook_memory/patterns", {
|
||||||
|
query: `${role} in ${city}, ${state}`,
|
||||||
|
top_k_playbooks: 25,
|
||||||
|
min_trait_frequency: 0.3,
|
||||||
|
}).catch(() => ({} as any));
|
||||||
|
|
||||||
|
const sources = (searchRes.sources || []).slice(0, 5).map((s: any) => {
|
||||||
|
const name = String(s.chunk_text || "").split("—")[0]?.trim() || s.doc_id;
|
||||||
|
return {
|
||||||
|
doc_id: s.doc_id,
|
||||||
|
name,
|
||||||
|
score: s.score,
|
||||||
|
playbook_boost: s.playbook_boost || 0,
|
||||||
|
playbook_citations: s.playbook_citations || [],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
contracts.push({
|
||||||
|
permit: {
|
||||||
|
cost,
|
||||||
|
work_type: p.work_type || "General construction",
|
||||||
|
description: (p.work_description || "").substring(0, 140),
|
||||||
|
address: `${p.street_number || ""} ${p.street_direction || ""} ${p.street_name || ""}`.trim(),
|
||||||
|
community_area: p.community_area,
|
||||||
|
issue_date: (p.issue_date || "").substring(0, 10),
|
||||||
|
},
|
||||||
|
proposed: {
|
||||||
|
role,
|
||||||
|
count,
|
||||||
|
city, state,
|
||||||
|
pool_size: searchRes.sql_matches,
|
||||||
|
candidates: sources,
|
||||||
|
},
|
||||||
|
discovered_pattern: patternRes.discovered_pattern,
|
||||||
|
pattern_matched: patternRes.matched_playbooks ?? 0,
|
||||||
|
pattern_workers_examined: patternRes.total_workers_examined ?? 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok({
|
||||||
|
generated_at: new Date().toISOString(),
|
||||||
|
count: contracts.length,
|
||||||
|
contracts,
|
||||||
|
duration_ms: Date.now() - start,
|
||||||
|
note: "Live Chicago permits paired with workers_500k-ranked candidates and playbook_memory discovered patterns. The permit is real public data; the proposed fill is derived per industry heuristic (~$150K → 1 worker).",
|
||||||
|
});
|
||||||
|
} catch (e: any) {
|
||||||
|
return err(`permit_contracts: ${e.message}`, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Intelligence: Log a search → selection as a learned pattern
|
// Intelligence: Log a search → selection as a learned pattern
|
||||||
if (url.pathname === "/intelligence/learn" && req.method === "POST") {
|
if (url.pathname === "/intelligence/learn" && req.method === "POST") {
|
||||||
const b = await json();
|
const b = await json();
|
||||||
|
|||||||
@ -112,6 +112,14 @@ body{font-family:'Inter',-apple-system,system-ui,'Segoe UI',sans-serif;backgroun
|
|||||||
|
|
||||||
<div id="main"><div class="ld">Analyzing contracts and workers...</div></div>
|
<div id="main"><div class="ld">Analyzing contracts and workers...</div></div>
|
||||||
|
|
||||||
|
<div class="section" id="live-contracts-section">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-title">Live Contracts — Chicago Permits → Proposed Fills</span>
|
||||||
|
<span class="section-meta">Real public permit data + our 500K worker bench + past playbook patterns</span>
|
||||||
|
</div>
|
||||||
|
<div id="live-contracts"><div class="ld">Loading live contracts...</div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="section" id="market-section">
|
<div class="section" id="market-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<span class="section-title">Market Intelligence</span>
|
<span class="section-title">Market Intelligence</span>
|
||||||
@ -147,12 +155,84 @@ var P=location.pathname.indexOf('/lakehouse')>=0?'/lakehouse':'';
|
|||||||
var A=location.origin+P;
|
var A=location.origin+P;
|
||||||
var AC=['#1a2744','#1a3a2a','#2a1a3a','#3a2a1a','#1a3a3a','#2a2a1a'];
|
var AC=['#1a2744','#1a3a2a','#2a1a3a','#3a2a1a','#1a3a3a','#2a2a1a'];
|
||||||
var lastQuery='';
|
var lastQuery='';
|
||||||
window.addEventListener('load',function(){loadDay();loadMarket();loadLearning()});
|
window.addEventListener('load',function(){loadDay();loadLiveContracts();loadMarket();loadLearning()});
|
||||||
|
|
||||||
function api(path,body){
|
function api(path,body){
|
||||||
return fetch(A+path,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}).then(function(r){return r.json()})
|
return fetch(A+path,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}).then(function(r){return r.json()})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadLiveContracts(){
|
||||||
|
// Pair live Chicago permits with our 500K worker bench and the
|
||||||
|
// meta-index discovered patterns for each role+geo. This is the
|
||||||
|
// "real external data meets synthetic playbook learning" card set.
|
||||||
|
api('/intelligence/permit_contracts',{}).then(function(r){
|
||||||
|
var el=document.getElementById('live-contracts');el.textContent='';
|
||||||
|
if(!r||!r.contracts||r.contracts.length===0){
|
||||||
|
el.textContent='No permits returned.';return;
|
||||||
|
}
|
||||||
|
r.contracts.forEach(function(c){
|
||||||
|
var p=c.permit||{}, prop=c.proposed||{};
|
||||||
|
var card=document.createElement('div');card.className='insight info';
|
||||||
|
card.style.cssText='background:#0d1117;border:1px solid #171d27;border-radius:10px;padding:16px;margin-bottom:10px;border-left:3px solid #388bfd';
|
||||||
|
// Header — permit
|
||||||
|
var hdr=document.createElement('div');hdr.style.cssText='display:flex;justify-content:space-between;margin-bottom:8px;gap:12px';
|
||||||
|
var left=document.createElement('div');
|
||||||
|
var title=document.createElement('div');title.style.cssText='font-weight:600;color:#e6edf3;font-size:14px';
|
||||||
|
title.textContent='$'+(p.cost||0).toLocaleString()+' · '+(p.work_type||'');
|
||||||
|
var addr=document.createElement('div');addr.style.cssText='color:#8b949e;font-size:12px;margin-top:2px';
|
||||||
|
addr.textContent=(p.address||'')+' · Chicago, IL · '+(p.issue_date||'');
|
||||||
|
left.appendChild(title);left.appendChild(addr);
|
||||||
|
var right=document.createElement('div');right.style.cssText='color:#58a6ff;font-size:12px;font-weight:600;text-align:right;white-space:nowrap';
|
||||||
|
right.textContent=prop.count+'× '+prop.role;
|
||||||
|
var sub=document.createElement('div');sub.style.cssText='color:#545d68;font-size:10px;text-align:right';
|
||||||
|
sub.textContent='pool: '+(prop.pool_size||'?').toLocaleString()+' available';
|
||||||
|
right.appendChild(sub);
|
||||||
|
hdr.appendChild(left);hdr.appendChild(right);card.appendChild(hdr);
|
||||||
|
// Description
|
||||||
|
if(p.description){
|
||||||
|
var desc=document.createElement('div');desc.style.cssText='color:#94a3b8;font-size:11px;margin-bottom:10px;line-height:1.5';
|
||||||
|
desc.textContent=p.description;card.appendChild(desc);
|
||||||
|
}
|
||||||
|
// Pattern (meta-index) chip
|
||||||
|
if(c.discovered_pattern){
|
||||||
|
var pat=document.createElement('div');pat.style.cssText='background:#0d2818;border:1px solid #2ea04360;border-radius:6px;padding:8px 12px;margin-bottom:10px;font-size:11px;color:#86efac;line-height:1.5';
|
||||||
|
var plabel=document.createElement('span');plabel.style.cssText='color:#3fb950;font-weight:600;margin-right:6px';
|
||||||
|
plabel.textContent='MEMORY ('+c.pattern_matched+' playbooks):';
|
||||||
|
pat.appendChild(plabel);
|
||||||
|
pat.appendChild(document.createTextNode(' '+c.discovered_pattern));
|
||||||
|
card.appendChild(pat);
|
||||||
|
}
|
||||||
|
// Candidates
|
||||||
|
var cands=prop.candidates||[];
|
||||||
|
cands.slice(0,3).forEach(function(cand,i){
|
||||||
|
var row=document.createElement('div');row.style.cssText='display:flex;align-items:center;gap:10px;padding:6px 10px;background:#161b22;border-radius:6px;margin-bottom:4px;font-size:12px';
|
||||||
|
var av=document.createElement('div');av.style.cssText='width:28px;height:28px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:10px;color:#e6edf3;background:'+AC[i%AC.length];
|
||||||
|
av.textContent=(cand.name||'?').split(' ').map(function(n){return (n[0]||'').toUpperCase()}).join('').substring(0,2);
|
||||||
|
var info=document.createElement('div');info.style.cssText='flex:1;min-width:0';
|
||||||
|
var nm=document.createElement('div');nm.style.cssText='color:#e6edf3;font-weight:500';nm.textContent=cand.name||cand.doc_id;
|
||||||
|
if((cand.playbook_boost||0)>0){
|
||||||
|
var chip=document.createElement('span');chip.style.cssText='margin-left:8px;padding:2px 7px;border-radius:9px;font-size:9px;font-weight:600;background:#0d2818;border:1px solid #2ea043;color:#3fb950;vertical-align:middle';
|
||||||
|
chip.textContent='Endorsed · '+(cand.playbook_citations||[]).length+' playbook'+((cand.playbook_citations||[]).length===1?'':'s');
|
||||||
|
nm.appendChild(chip);
|
||||||
|
}
|
||||||
|
var sub2=document.createElement('div');sub2.style.cssText='color:#545d68;font-size:10px';
|
||||||
|
sub2.textContent=cand.doc_id+' · score '+(cand.score||0).toFixed(3);
|
||||||
|
info.appendChild(nm);info.appendChild(sub2);
|
||||||
|
row.appendChild(av);row.appendChild(info);
|
||||||
|
card.appendChild(row);
|
||||||
|
});
|
||||||
|
if(cands.length>3){
|
||||||
|
var more=document.createElement('div');more.style.cssText='font-size:10px;color:#58a6ff;padding:4px 10px;margin-top:2px';
|
||||||
|
more.textContent='+ '+(cands.length-3)+' more candidates available';
|
||||||
|
card.appendChild(more);
|
||||||
|
}
|
||||||
|
el.appendChild(card);
|
||||||
|
});
|
||||||
|
}).catch(function(e){
|
||||||
|
document.getElementById('live-contracts').textContent='Error loading: '+e.message;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function loadDay(){
|
function loadDay(){
|
||||||
// Step 1: run simulation + get real worker count + populate dropdowns from actual data
|
// Step 1: run simulation + get real worker count + populate dropdowns from actual data
|
||||||
Promise.all([
|
Promise.all([
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user