Add files via upload
adding dashboard copy link adding copy get route for dashboard
This commit is contained in:
parent
190fa874c7
commit
2f09dbe22c
2 changed files with 82 additions and 21 deletions
54
router.py
54
router.py
|
|
@ -679,20 +679,38 @@ async def copy_proxy(request: Request):
|
|||
# 3. Iterate over all endpoints to copy the model on each endpoint
|
||||
status_list = []
|
||||
for endpoint in config.endpoints:
|
||||
client = ollama.AsyncClient(host=endpoint)
|
||||
# 4. Proxy a simple copy request
|
||||
copy = await client.copy(source=src, destination=dst)
|
||||
status_list.append(copy.status)
|
||||
if "/v1" not in endpoint:
|
||||
client = ollama.AsyncClient(host=endpoint)
|
||||
# 4. Proxy a simple copy request
|
||||
copy = await client.copy(source=src, destination=dst)
|
||||
status_list.append(copy.status)
|
||||
|
||||
# 4. Return with 200 OK if all went well, 404 if a single endpoint failed
|
||||
if 404 in status_list:
|
||||
return Response(
|
||||
status_code=404
|
||||
)
|
||||
else:
|
||||
return Response(
|
||||
status_code=200
|
||||
)
|
||||
return Response(status_code=404 if 404 in status_list else 200)
|
||||
|
||||
@app.get("/api/copy")
|
||||
async def copy_proxy_from_dashboard(source: str, destination: str):
|
||||
"""
|
||||
Proxy a model copy request to each Ollama endpoint and reply with a status code.
|
||||
Accepts `source` and `destination` exclusively as query‑string parameters.
|
||||
"""
|
||||
# 1. Validate that both values are non‑empty strings (FastAPI already guarantees presence)
|
||||
if not source:
|
||||
raise HTTPException(status_code=400, detail="Missing required query parameter 'source'")
|
||||
if not destination:
|
||||
raise HTTPException(status_code=400, detail="Missing required query parameter 'destination'")
|
||||
|
||||
# 2. Iterate over all endpoints to copy the model on each endpoint
|
||||
status_list = []
|
||||
for endpoint in config.endpoints:
|
||||
if "/v1" not in endpoint:
|
||||
client = ollama.AsyncClient(host=endpoint)
|
||||
# 3. Proxy a simple copy request
|
||||
copy = await client.copy(source=source, destination=destination)
|
||||
status_list.append(copy.status)
|
||||
|
||||
# 4. Return with 200 OK if all went well, 404 if any endpoint failed
|
||||
return Response(status_code=404 if 404 in status_list else 200)
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# 13. API route – Delete
|
||||
|
|
@ -720,10 +738,11 @@ async def delete_proxy(request: Request):
|
|||
# 2. Iterate over all endpoints to delete the model on each endpoint
|
||||
status_list = []
|
||||
for endpoint in config.endpoints:
|
||||
client = ollama.AsyncClient(host=endpoint)
|
||||
# 3. Proxy a simple copy request
|
||||
copy = await client.delete(model=model)
|
||||
status_list.append(copy.status)
|
||||
if "/v1" not in endpoint:
|
||||
client = ollama.AsyncClient(host=endpoint)
|
||||
# 3. Proxy a simple copy request
|
||||
copy = await client.delete(model=model)
|
||||
status_list.append(copy.status)
|
||||
|
||||
# 4. Retrun 200 0K, if a single enpoint fails, respond with 404
|
||||
if 404 in status_list:
|
||||
|
|
@ -1005,7 +1024,6 @@ async def openai_chat_completions_proxy(request: Request):
|
|||
params = {
|
||||
"messages": messages,
|
||||
"model": model,
|
||||
"seed": seed,
|
||||
"stop": stop,
|
||||
"stream": stream,
|
||||
}
|
||||
|
|
@ -1024,6 +1042,8 @@ async def openai_chat_completions_proxy(request: Request):
|
|||
params["temperature"] = temperature
|
||||
if top_p is not None:
|
||||
params["top_p"] = top_p
|
||||
if seed is not None:
|
||||
params["seed"] = seed
|
||||
if presence_penalty is not None:
|
||||
params["presence_penalty"] = presence_penalty
|
||||
if frequency_penalty is not None:
|
||||
|
|
|
|||
|
|
@ -46,8 +46,16 @@
|
|||
}
|
||||
}
|
||||
/* Add a tiny status‑style section */
|
||||
.status-ok { color: #006400; font-weight: bold; } /* dark green */
|
||||
.status-error{ color: #8B0000; font-weight: bold; } /* dark red */
|
||||
.status-ok { color: #006400; font-weight: bold; } /* dark green */
|
||||
.status-error{ color: #8B0000; font-weight: bold; } /* dark red */
|
||||
.copy-link {
|
||||
font-size:0.9em;
|
||||
margin-left:0.5em;
|
||||
color:#0066cc;
|
||||
cursor:pointer;
|
||||
text-decoration:underline;
|
||||
}
|
||||
.copy-link:hover { text-decoration:none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -130,9 +138,42 @@ async function loadTags(){
|
|||
try{
|
||||
const data = await fetchJSON('/api/tags');
|
||||
const body = document.getElementById('tags-body');
|
||||
body.innerHTML = data.models.map(m=>`<tr><td class="model">${m.id || m.name}</td><td>${m.digest}</td></tr>`).join('');
|
||||
const countSpan = document.getElementById('tags-count');
|
||||
body.innerHTML = data.models.map(m => {
|
||||
// Build the model cell
|
||||
let modelCell = `${m.id || m.name}`;
|
||||
|
||||
// Add the copy link *only if a digest exists*
|
||||
if (m.digest) {
|
||||
modelCell += `
|
||||
<a href="#" class="copy-link" data-source="${m.name}">
|
||||
copy
|
||||
</a>`;
|
||||
}
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td class="model">${modelCell}</td>
|
||||
<td>${m.digest || ''}</td>
|
||||
</tr>`;
|
||||
}).join(''); const countSpan = document.getElementById('tags-count');
|
||||
countSpan.textContent = `${data.models.length}`;
|
||||
// Attach copy‑link handlers
|
||||
document.querySelectorAll('.copy-link').forEach(link => {
|
||||
link.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
const source = link.dataset.source;
|
||||
const dest = prompt(`Enter destination for ${source}:`);
|
||||
if (!dest) return; // cancel if empty
|
||||
try{
|
||||
const resp = await fetch(`/api/copy?source=${encodeURIComponent(source)}&destination=${encodeURIComponent(dest)}`);
|
||||
if (!resp.ok) throw new Error(`Copy failed: ${resp.status}`);
|
||||
alert(`Copied ${source} to ${dest} successfully.`);
|
||||
}catch(err){
|
||||
console.error(err);
|
||||
alert(`Error copying ${source} to ${dest}: ${err}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}catch(e){ console.error(e); }
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue