Add Deep Research tool — agent + rank/rerank RAG
New surface at /deep-research.php where the user pastes a question or uploads PDF/DOCX/TXT case files and a LLM-orchestrated agent researches the Do Better Norge legal corpus from 3-5 angles, with hybrid retrieval, cross-encoder rerank, and synthesis that emits an inline-[n]-cited markdown brief plus a numbered sources panel. Uploaded documents are chunked + embedded in memory only (nomic-embed-text via LiteLLM) and searched alongside the shared corpus during the same request — never persisted to disk, DB, or Qdrant. Reuses ClientRagPipeline::searchAll (hybrid + rerank), dbnV6 slice helpers, and the existing extract.php text-extraction logic via a new dbnToolsExtractUploadedFile() helper. Also adds dbnToolsCallGpuLlm() helper in bootstrap.php — fixes a latent bug where LegalTools.php was already calling that name with no definition. Search.php is unchanged.
This commit is contained in:
@@ -1701,3 +1701,478 @@ p {
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
Deep Research — agent + rank/rerank RAG surface
|
||||
========================================================================= */
|
||||
|
||||
.deep-research .lang-switcher {
|
||||
display: inline-flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.deep-research .lang-btn {
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
background: #fff;
|
||||
border: 1px solid var(--line);
|
||||
color: var(--muted);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.deep-research .lang-btn.is-active {
|
||||
background: var(--soft-teal);
|
||||
color: var(--teal-dark);
|
||||
border-color: rgba(15, 118, 110, 0.30);
|
||||
}
|
||||
|
||||
.dr-slice-section {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dr-slice-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.dr-slice {
|
||||
text-align: left;
|
||||
background: #fbfcfe;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 12px 13px;
|
||||
cursor: pointer;
|
||||
min-height: 96px;
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
align-content: start;
|
||||
transition: border-color 120ms ease, background 120ms ease;
|
||||
}
|
||||
|
||||
.dr-slice:hover {
|
||||
border-color: rgba(15, 118, 110, 0.30);
|
||||
}
|
||||
|
||||
.dr-slice.is-on {
|
||||
background: var(--soft-teal);
|
||||
border-color: rgba(15, 118, 110, 0.45);
|
||||
}
|
||||
|
||||
.dr-slice__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dr-slice__title {
|
||||
font-weight: 800;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.dr-slice__badge {
|
||||
background: #fff;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 999px;
|
||||
color: var(--muted);
|
||||
font-size: 0.66rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.06em;
|
||||
padding: 3px 8px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.dr-slice.is-on .dr-slice__badge {
|
||||
background: var(--teal);
|
||||
border-color: var(--teal);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dr-slice__tagline {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.advanced-panel .dr-control-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.dr-control-card {
|
||||
background: #fbfcfe;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.dr-control-card label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
font-weight: 800;
|
||||
color: var(--ink);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.dr-control-card small {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
color: var(--muted);
|
||||
font-size: 0.74rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.dr-control-card input[type="range"] {
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
accent-color: var(--teal);
|
||||
}
|
||||
|
||||
.dr-control-value {
|
||||
color: var(--coral);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.advanced-panel .dr-control-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
.dr-slice-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.deep-research-results {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.dr-result-block {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.dr-brief {
|
||||
line-height: 1.65;
|
||||
color: var(--ink);
|
||||
font-size: 1.0rem;
|
||||
}
|
||||
|
||||
.dr-brief p {
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
|
||||
.dr-brief code {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
background: var(--soft-teal);
|
||||
padding: 1px 5px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.86em;
|
||||
}
|
||||
|
||||
.dr-brief strong { color: var(--ink); }
|
||||
.dr-brief em { color: var(--muted); }
|
||||
|
||||
.dr-cite {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
margin: 0 1px;
|
||||
padding: 0 5px;
|
||||
border-radius: 999px;
|
||||
background: var(--soft-coral);
|
||||
color: var(--coral);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 800;
|
||||
font-variant-numeric: tabular-nums;
|
||||
cursor: pointer;
|
||||
border: 1px solid rgba(194, 65, 12, 0.25);
|
||||
vertical-align: 1px;
|
||||
}
|
||||
|
||||
.dr-cite:hover { background: var(--coral); color: #fff; }
|
||||
|
||||
.dr-sources-head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.dr-sources-head h3 {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.dr-sources-head small {
|
||||
color: var(--muted);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.dr-source-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.dr-source-card {
|
||||
display: grid;
|
||||
grid-template-columns: 34px 1fr auto;
|
||||
gap: 12px;
|
||||
align-items: start;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
background: #fbfcfe;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dr-source-card:hover { border-color: rgba(15, 118, 110, 0.40); }
|
||||
|
||||
.dr-source-card.is-highlight {
|
||||
border-color: var(--coral);
|
||||
background: var(--soft-coral);
|
||||
}
|
||||
|
||||
.dr-source-number {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 999px;
|
||||
background: var(--soft-coral);
|
||||
color: var(--coral);
|
||||
font-weight: 900;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.dr-source-body {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.dr-source-title {
|
||||
font-weight: 800;
|
||||
color: var(--ink);
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.dr-source-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.dr-source-tag {
|
||||
background: var(--soft-teal);
|
||||
color: var(--teal-dark);
|
||||
border-radius: 999px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 800;
|
||||
padding: 3px 8px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.dr-source-tag--upload { background: #fff0e8; color: #8a4524; }
|
||||
.dr-source-tag--score { background: #eef3fb; color: #314158; }
|
||||
|
||||
.dr-source-excerpt {
|
||||
color: var(--muted);
|
||||
margin-top: 8px;
|
||||
line-height: 1.5;
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
.dr-source-aside {
|
||||
align-self: stretch;
|
||||
display: grid;
|
||||
grid-template-rows: auto auto;
|
||||
gap: 6px;
|
||||
font-size: 0.78rem;
|
||||
color: var(--muted);
|
||||
text-align: right;
|
||||
min-width: 90px;
|
||||
}
|
||||
|
||||
.dr-source-aside b {
|
||||
color: var(--ink);
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
/* Method trace — overrides for #traceList rendered in rich mode */
|
||||
.trace-list.is-rich {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.trace-list.is-rich .trace-step {
|
||||
display: grid;
|
||||
grid-template-columns: 28px 1fr;
|
||||
gap: 10px;
|
||||
align-items: start;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: #fbfcfe;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.trace-list.is-rich .trace-step__marker {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--line);
|
||||
background: #fff;
|
||||
color: var(--muted);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 900;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.trace-list.is-rich .trace-step__label {
|
||||
display: block;
|
||||
font-weight: 800;
|
||||
color: var(--ink);
|
||||
font-size: 0.94rem;
|
||||
}
|
||||
|
||||
.trace-list.is-rich .trace-step__detail {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
color: var(--muted);
|
||||
font-size: 0.83rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.trace-list.is-rich .trace-step.is-running {
|
||||
background: var(--soft-coral);
|
||||
}
|
||||
.trace-list.is-rich .trace-step.is-running .trace-step__marker {
|
||||
background: rgba(194, 65, 12, 0.18);
|
||||
border-color: rgba(194, 65, 12, 0.35);
|
||||
color: var(--coral);
|
||||
animation: drTracePulse 950ms ease-in-out infinite;
|
||||
}
|
||||
|
||||
.trace-list.is-rich .trace-step.is-done .trace-step__marker {
|
||||
background: var(--soft-teal);
|
||||
border-color: rgba(15, 118, 110, 0.30);
|
||||
color: var(--teal-dark);
|
||||
}
|
||||
|
||||
.trace-list.is-rich .trace-step.is-warning .trace-step__marker {
|
||||
background: #fff4dc;
|
||||
border-color: rgba(183, 121, 31, 0.35);
|
||||
color: var(--amber);
|
||||
}
|
||||
|
||||
.trace-list.is-rich .trace-step.is-error {
|
||||
background: #fff0e8;
|
||||
}
|
||||
.trace-list.is-rich .trace-step.is-error .trace-step__marker {
|
||||
background: rgba(180, 30, 30, 0.10);
|
||||
border-color: rgba(180, 30, 30, 0.30);
|
||||
color: #b41e1e;
|
||||
}
|
||||
|
||||
@keyframes drTracePulse {
|
||||
0%, 100% { opacity: 0.55; transform: scale(0.92); }
|
||||
50% { opacity: 1; transform: scale(1.04); }
|
||||
}
|
||||
|
||||
/* Source modal */
|
||||
.dr-source-modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(23, 32, 51, 0.62);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.dr-source-modal__dialog {
|
||||
width: min(960px, 100%);
|
||||
max-height: 90vh;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 28px 92px rgba(0, 0, 0, 0.34);
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
||||
.dr-source-modal__head {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
justify-content: space-between;
|
||||
gap: 14px;
|
||||
padding: 16px 18px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.dr-source-modal__head h3 {
|
||||
margin: 0;
|
||||
color: var(--ink);
|
||||
line-height: 1.25;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.dr-source-modal__body {
|
||||
display: grid;
|
||||
grid-template-columns: 260px minmax(0, 1fr);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dr-source-modal__meta,
|
||||
.dr-source-modal__text {
|
||||
padding: 16px 18px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dr-source-modal__meta {
|
||||
border-right: 1px solid var(--line);
|
||||
background: #fbfcfe;
|
||||
color: var(--muted);
|
||||
font-size: 0.88rem;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.dr-source-modal__meta dt {
|
||||
color: var(--ink);
|
||||
font-weight: 800;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.dr-source-modal__meta dt:first-of-type { margin-top: 0; }
|
||||
|
||||
.dr-source-modal__text {
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.7;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.dr-source-modal__body { grid-template-columns: 1fr; }
|
||||
.dr-source-modal__meta { border-right: 0; border-bottom: 1px solid var(--line); }
|
||||
.dr-source-card { grid-template-columns: 32px 1fr; }
|
||||
.dr-source-aside { display: none; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user