Download tools/stackoverflow_tool.py from sedayzc/developer-asisstant-tool-call: direct link, hf CLI and curl.
- Browser
- Download file 5.57 kB
-
https://huggingface.co/spaces/sedayzc/developer-asisstant-tool-call/resolve/main/tools/stackoverflow_tool.py
- Command line
-
hf download hf://spaces/sedayzc/developer-asisstant-tool-call/tools/stackoverflow_tool.py
-
curl -L -o stackoverflow_tool.py https://huggingface.co/spaces/sedayzc/developer-asisstant-tool-call/resolve/main/tools/stackoverflow_tool.py
5.57 kB
| from datetime import datetime, timezone | |
| from html import unescape | |
| import re | |
| from typing import Any | |
| import requests | |
| from config import STACKEXCHANGE_API | |
| def _unix_to_iso(timestamp: int | None) -> str | None: | |
| if timestamp is None: | |
| return None | |
| try: | |
| return datetime.fromtimestamp( | |
| timestamp, | |
| tz=timezone.utc, | |
| ).isoformat() | |
| except (TypeError, ValueError, OSError): | |
| return None | |
| def _normalize_text(text: str) -> str: | |
| """ | |
| Karşılaştırma için metni sadeleştirir. | |
| """ | |
| return re.sub( | |
| r"[^a-z0-9\s]", | |
| " ", | |
| text.lower(), | |
| ) | |
| def _calculate_relevance_score( | |
| query: str, | |
| title: str, | |
| tags: list[str], | |
| ) -> int: | |
| """ | |
| Sorgu kelimeleriyle başlık ve etiketler arasındaki | |
| basit eşleşme puanını hesaplar. | |
| """ | |
| ignored_words = { | |
| "the", | |
| "a", | |
| "an", | |
| "is", | |
| "in", | |
| "of", | |
| "to", | |
| "and", | |
| "or", | |
| "python", | |
| "typeerror", | |
| "error", | |
| } | |
| normalized_query = _normalize_text(query) | |
| normalized_title = _normalize_text(title) | |
| query_words = { | |
| word | |
| for word in normalized_query.split() | |
| if len(word) > 2 and word not in ignored_words | |
| } | |
| title_words = set(normalized_title.split()) | |
| normalized_tags = { | |
| tag.lower() | |
| for tag in tags | |
| } | |
| title_matches = len( | |
| query_words.intersection(title_words) | |
| ) | |
| tag_matches = len( | |
| query_words.intersection(normalized_tags) | |
| ) | |
| exact_phrase_bonus = ( | |
| 5 | |
| if normalized_query in normalized_title | |
| else 0 | |
| ) | |
| return ( | |
| title_matches * 3 | |
| + tag_matches * 2 | |
| + exact_phrase_bonus | |
| ) | |
| def search_stackoverflow( | |
| query: str, | |
| limit: int = 5, | |
| ) -> dict[str, Any]: | |
| """ | |
| Stack Overflow üzerinde verilen sorguyla ilgili | |
| soruları arar. | |
| """ | |
| query = query.strip() | |
| if not query: | |
| raise ValueError( | |
| "Stack Overflow arama sorgusu boş olamaz." | |
| ) | |
| limit = max(1, min(limit, 10)) | |
| url = f"{STACKEXCHANGE_API}/search/advanced" | |
| # Nihai limitten daha fazla sonuç alıp | |
| # kendi relevance sıralamamızı uyguluyoruz. | |
| candidate_limit = min(limit * 4, 40) | |
| params = { | |
| "site": "stackoverflow", | |
| "q": query, | |
| "sort": "relevance", | |
| "order": "desc", | |
| "pagesize": candidate_limit, | |
| "filter": "withbody", | |
| } | |
| try: | |
| response = requests.get( | |
| url, | |
| params=params, | |
| timeout=20, | |
| ) | |
| response.raise_for_status() | |
| except requests.Timeout: | |
| return { | |
| "success": False, | |
| "query": query, | |
| "error": ( | |
| "Stack Overflow API isteği zaman aşımına uğradı." | |
| ), | |
| } | |
| except requests.RequestException as exc: | |
| return { | |
| "success": False, | |
| "query": query, | |
| "error": ( | |
| "Stack Overflow API isteği başarısız oldu: " | |
| f"{exc}" | |
| ), | |
| } | |
| try: | |
| data = response.json() | |
| except ValueError: | |
| return { | |
| "success": False, | |
| "query": query, | |
| "error": ( | |
| "Stack Overflow API geçerli bir JSON " | |
| "yanıtı döndürmedi." | |
| ), | |
| } | |
| if "error_message" in data: | |
| return { | |
| "success": False, | |
| "query": query, | |
| "error_id": data.get("error_id"), | |
| "error_name": data.get("error_name"), | |
| "error": data.get("error_message"), | |
| } | |
| questions = [] | |
| for item in data.get("items", []): | |
| owner_data = item.get("owner") or {} | |
| tags = item.get("tags", []) | |
| title = unescape(item.get("title", "")) | |
| relevance_score = _calculate_relevance_score( | |
| query=query, | |
| title=title, | |
| tags=tags, | |
| ) | |
| questions.append( | |
| { | |
| "question_id": item.get("question_id"), | |
| "title": title, | |
| "link": item.get("link"), | |
| "score": item.get("score", 0), | |
| "relevance_score": relevance_score, | |
| "answer_count": item.get("answer_count", 0), | |
| "is_answered": item.get("is_answered", False), | |
| "accepted_answer_id": item.get( | |
| "accepted_answer_id" | |
| ), | |
| "view_count": item.get("view_count", 0), | |
| "tags": tags, | |
| "owner": owner_data.get("display_name"), | |
| "body": item.get("body"), | |
| "creation_date": _unix_to_iso( | |
| item.get("creation_date") | |
| ), | |
| "last_activity_date": _unix_to_iso( | |
| item.get("last_activity_date") | |
| ), | |
| } | |
| ) | |
| questions.sort( | |
| key=lambda question: ( | |
| question["relevance_score"], | |
| question["score"], | |
| ), | |
| reverse=True, | |
| ) | |
| selected_questions = questions[:limit] | |
| return { | |
| "success": True, | |
| "query": query, | |
| "returned_count": len(selected_questions), | |
| "candidate_count": len(questions), | |
| "has_more": data.get("has_more", False), | |
| "quota_remaining": data.get("quota_remaining"), | |
| "quota_max": data.get("quota_max"), | |
| "backoff": data.get("backoff"), | |
| "questions": selected_questions, | |
| } |