Spaces:
Sleeping
Sleeping
First version
Browse files- Dockerfile +13 -0
- app.py +83 -0
- requirements.txt +4 -0
Dockerfile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11.3
|
| 2 |
+
|
| 3 |
+
RUN useradd -m -u 1000 user
|
| 4 |
+
USER user
|
| 5 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
| 6 |
+
|
| 7 |
+
WORKDIR /app
|
| 8 |
+
|
| 9 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
| 10 |
+
RUN pip install --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org --no-cache-dir --upgrade -r requirements.txt
|
| 11 |
+
|
| 12 |
+
COPY --chown=user . /app
|
| 13 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
app.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import *
|
| 2 |
+
import httpx
|
| 3 |
+
from mcp.server.fastmcp import FastMCP
|
| 4 |
+
|
| 5 |
+
server = FastMCP(name="streamable-http-mcp-server-test", json_response=False, stateless_http=False)
|
| 6 |
+
|
| 7 |
+
async def make_request(url: str, data: Dict[str, Any]):
|
| 8 |
+
headers = {"Accept": "application/json"}
|
| 9 |
+
async with httpx.AsyncClient(verify=False) as client:
|
| 10 |
+
try:
|
| 11 |
+
response = await client.post(url, headers=headers, json=data)
|
| 12 |
+
response.raise_for_status()
|
| 13 |
+
return response.json()
|
| 14 |
+
except:
|
| 15 |
+
return None
|
| 16 |
+
|
| 17 |
+
@server.tool()
|
| 18 |
+
async def search_academic_papers_arxiv(keyword: str, limit: int = 5) -> str:
|
| 19 |
+
"""
|
| 20 |
+
Search papers from arXiv database with specified keywords [optional: a limit of papers the user wants]
|
| 21 |
+
Args: keyword: string, [optional: limit: integer, set limit to 5 if not specified]
|
| 22 |
+
"""
|
| 23 |
+
response = await make_request("https://organizedprogrammers-arxiv.hf.space/search", {"keyword": keyword, "limit": limit})
|
| 24 |
+
if not response:
|
| 25 |
+
return "Unable to find papers | No papers has been found"
|
| 26 |
+
return "\n".join([f"arXiv n°{paper_id} - {paper_meta['title']} by {paper_meta['authors']} : {paper_meta['abstract']}" for paper_id, paper_meta in response['message'].items()])
|
| 27 |
+
|
| 28 |
+
@server.tool()
|
| 29 |
+
async def get_arxiv_pub_text(arxiv_id: str) -> str:
|
| 30 |
+
"""
|
| 31 |
+
Extract publication PDF via arXiv ID
|
| 32 |
+
Returns the full content of the publication
|
| 33 |
+
Args: arxiv_id -> string
|
| 34 |
+
"""
|
| 35 |
+
response = await make_request("https://organizedprogrammers-arxiv.hf.space/extract_pdf/arxiv_id", {"doc_id": arxiv_id})
|
| 36 |
+
if not response:
|
| 37 |
+
return "Unable to extract PDF | arXiv PDF not found"
|
| 38 |
+
return response["message"]["text"]
|
| 39 |
+
|
| 40 |
+
@server.tool()
|
| 41 |
+
async def get_document_url(doc_id: str) -> str:
|
| 42 |
+
"""
|
| 43 |
+
Find technical document or specification from 3GPP / ETSI / GP by a document ID
|
| 44 |
+
Returns the URL (also scope + version if doc is a specification [not all specifications have a version or scope])
|
| 45 |
+
Arguments: doc_id -> string
|
| 46 |
+
"""
|
| 47 |
+
response = await make_request('https://organizedprogrammers-docfinder.hf.space/find/single', {"doc_id": doc_id})
|
| 48 |
+
if not response:
|
| 49 |
+
return "Unable to find document/specification"
|
| 50 |
+
version = response.get('version', 'unavailable')
|
| 51 |
+
scope = response.get('scope', 'unavailable')
|
| 52 |
+
return f'Downloadable !\nDoc No. {doc_id}\nURL : {response.get("url")}\nVersion : {version}\nScope : {scope}'
|
| 53 |
+
|
| 54 |
+
@server.tool()
|
| 55 |
+
async def search_specifications_with_keywords(keywords: str, threshold: int = 60, source: Literal["3GPP", "ETSI", "all"] = "all", spec_type: Optional[Literal["TS", "TR"]] = None):
|
| 56 |
+
"""
|
| 57 |
+
Search specifications from 3GPP and/or ETSI with keywoeds (Based off BM25 scoring)
|
| 58 |
+
Returns a list of specifications metadata that matches the similarity score threshold, the keywords, the source and specification type
|
| 59 |
+
Arguments:
|
| 60 |
+
- keywords -> string
|
| 61 |
+
- threshold -> integer (by default, set to 60) [between 0-100]
|
| 62 |
+
- source -> string (either '3GPP', 'ETSI' or 'all', by default, set to 'all')
|
| 63 |
+
- spec_type -> string (either 'TS' or 'TR' or None, by default, set to None)
|
| 64 |
+
"""
|
| 65 |
+
response = await make_request('https://organizedprogrammers-docfinder.hf.space/search/bm25', {"keywords": keywords, "threshold": threshold, "source": source, "spec_type": spec_type})
|
| 66 |
+
if not response:
|
| 67 |
+
return "Unable to search specifications | No specifications has been found"
|
| 68 |
+
results = response["results"]
|
| 69 |
+
return "\n---\n".join([f"Specification ID: {spec['id']}\nTitle: {spec['title']}\nType: {'Technical Specification' if spec['spec_type'] == 'TS' else 'Technical Report'}\nVersion: {spec.get('version', 'unavailable')}\nScope: {spec.get('scope', 'unavailable')}\nWorking Group: {spec.get('working_group', 'not defined')}\nURL: {spec.get('url', 'unavailable')}" for spec in results])
|
| 70 |
+
|
| 71 |
+
@server.tool()
|
| 72 |
+
async def get_spec_text(spec_id: str) -> Union[Dict[str, str], str]:
|
| 73 |
+
"""
|
| 74 |
+
Extract specification from 3GPP or ETSI
|
| 75 |
+
Returns a dictionary k:v where k is the section (1., 2.2.1, ...) and v, the content of k, or a string if failed
|
| 76 |
+
Args: spec_id -> string
|
| 77 |
+
"""
|
| 78 |
+
response = await make_request('https://organizedprogrammers-specsplitter.hf.space/extract_text/structured', {"spec_id": spec_id})
|
| 79 |
+
if not response:
|
| 80 |
+
return "Unable to extract specification text"
|
| 81 |
+
return response
|
| 82 |
+
|
| 83 |
+
app = server.streamable_http_app
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
httpx
|
| 2 |
+
uvicorn[standard]
|
| 3 |
+
fastapi
|
| 4 |
+
mcp[cli]
|