diff --git a/developer-knowledge/README.md b/developer-knowledge/README.md new file mode 100644 index 0000000000..8345539d92 --- /dev/null +++ b/developer-knowledge/README.md @@ -0,0 +1,28 @@ +# Google Developer Knowledge API Python Samples + +This directory contains Python code samples demonstrating how to use the [Google Developer Knowledge API](https://developers.google.com/knowledge) client library (`google-developer-knowledge`). + +## Setup + +1. Enable the Developer Knowledge API on your Google Cloud project: + ```bash + gcloud services enable developerknowledge.googleapis.com + ``` + +2. Install dependencies: + ```bash + pip install -r requirements.txt + ``` + +## Samples + +* **[Search Document Chunks](search_document_chunks.py)**: Search public developer documentation chunks by query (`developerknowledge_search_document_chunks`). +* **[Get Document](get_document.py)**: Retrieve a single documentation page with full markdown content (`developerknowledge_get_document`). +* **[Batch Get Documents](batch_get_documents.py)**: Fetch multiple documentation pages in one call (`developerknowledge_batch_get_documents`). +* **[Answer Query](answer_query.py)**: Get a grounded, cited answer to a technical question (`developerknowledge_answer_query`). + +## Running Tests + +```bash +pytest +``` diff --git a/developer-knowledge/answer_query.py b/developer-knowledge/answer_query.py new file mode 100644 index 0000000000..350eed47e5 --- /dev/null +++ b/developer-knowledge/answer_query.py @@ -0,0 +1,49 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START developerknowledge_answer_query] +from google.cloud import developer_knowledge_v1 + + +def answer_query( + query: str = "How do I create a Google Cloud Storage bucket?", +) -> developer_knowledge_v1.AnswerQueryResponse: + """Answers a developer question grounded in Google developer documentation. + + Args: + query: The technical question to answer. + + Returns: + The AnswerQueryResponse containing the grounded answer, + citations, and references. + """ + client = developer_knowledge_v1.DeveloperKnowledgeClient() + + request = developer_knowledge_v1.AnswerQueryRequest( + query=query, + ) + + response = client.answer_query(request=request) + + print(f"Answer:\n{response.answer.answer_text}\n") + print(f"Citations count: {len(response.answer.citations)}") + print(f"References count: {len(response.answer.references)}") + + return response + + +# [END developerknowledge_answer_query] + +if __name__ == "__main__": + answer_query() diff --git a/developer-knowledge/answer_query_test.py b/developer-knowledge/answer_query_test.py new file mode 100644 index 0000000000..6d83c5a291 --- /dev/null +++ b/developer-knowledge/answer_query_test.py @@ -0,0 +1,27 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import answer_query + + +def test_answer_query(capsys): + response = answer_query.answer_query( + query="How to create a Cloud Storage bucket", + ) + out, _ = capsys.readouterr() + + assert response is not None + assert response.answer is not None + assert len(response.answer.answer_text) > 0 + assert "Answer:" in out diff --git a/developer-knowledge/batch_get_documents.py b/developer-knowledge/batch_get_documents.py new file mode 100644 index 0000000000..2ebe33daad --- /dev/null +++ b/developer-knowledge/batch_get_documents.py @@ -0,0 +1,57 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START developerknowledge_batch_get_documents] +from typing import List, Optional + +from google.cloud import developer_knowledge_v1 + + +def batch_get_documents( + names: Optional[List[str]] = None, +) -> developer_knowledge_v1.BatchGetDocumentsResponse: + """Retrieves multiple developer documentation pages in a single request. + + Args: + names: A list of resource names in format 'documents/{uri_without_scheme}'. + + Returns: + The BatchGetDocumentsResponse containing the retrieved documents. + """ + if names is None: + names = [ + "documents/docs.cloud.google.com/storage/docs/creating-buckets", + "documents/docs.cloud.google.com/storage/docs/deleting-buckets", + ] + + client = developer_knowledge_v1.DeveloperKnowledgeClient() + + request = developer_knowledge_v1.BatchGetDocumentsRequest( + names=names, + ) + + response = client.batch_get_documents(request=request) + + for doc in response.documents: + print(f"Title: {doc.title}") + print(f"URI: {doc.uri}") + print(f"Content Length: {doc.content_length_bytes} bytes\n") + + return response + + +# [END developerknowledge_batch_get_documents] + +if __name__ == "__main__": + batch_get_documents() diff --git a/developer-knowledge/batch_get_documents_test.py b/developer-knowledge/batch_get_documents_test.py new file mode 100644 index 0000000000..0771851b94 --- /dev/null +++ b/developer-knowledge/batch_get_documents_test.py @@ -0,0 +1,31 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import batch_get_documents + + +def test_batch_get_documents(capsys): + names = [ + "documents/docs.cloud.google.com/storage/docs/creating-buckets", + "documents/docs.cloud.google.com/storage/docs/deleting-buckets", + ] + response = batch_get_documents.batch_get_documents(names=names) + out, _ = capsys.readouterr() + + assert response is not None + assert len(response.documents) == 2 + for doc in response.documents: + assert doc.name in names + assert len(doc.title) > 0 + assert "Title:" in out diff --git a/developer-knowledge/conftest.py b/developer-knowledge/conftest.py new file mode 100644 index 0000000000..53122cb064 --- /dev/null +++ b/developer-knowledge/conftest.py @@ -0,0 +1,161 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Pytest configuration and local fallback mocks for developer_knowledge_v1.""" + +import sys +from unittest.mock import MagicMock + +try: + from google.cloud import developer_knowledge_v1 # noqa: F401 +except ImportError: + try: + import google.cloud + import google.developer_knowledge_v1 as dk + + google.cloud.developer_knowledge_v1 = dk + sys.modules["google.cloud.developer_knowledge_v1"] = dk + except ImportError: + pass + +if "google.cloud.developer_knowledge_v1" not in sys.modules: + mock_dk = MagicMock() + + class SearchDocumentChunksRequest: + def __init__(self, query="", page_size=5, page_token="", filter=""): + self.query = query + self.page_size = page_size + self.page_token = page_token + self.filter = filter + + class GetDocumentRequest: + def __init__(self, name=""): + self.name = name + + class BatchGetDocumentsRequest: + def __init__(self, names=None): + self.names = names or [] + + class AnswerQueryRequest: + def __init__(self, query="", filter=""): + self.query = query + self.filter = filter + + class DocumentChunk: + def __init__( + self, + parent="documents/docs.cloud.google.com/storage/docs/creating-buckets", + id="chunk-1", + content="To create a bucket, use the Google Cloud console or gcloud CLI.", + ): + self.parent = parent + self.id = id + self.content = content + + class Document: + def __init__( + self, + name="documents/docs.cloud.google.com/storage/docs/creating-buckets", + title="Creating Buckets", + uri="docs.cloud.google.com/storage/docs/creating-buckets", + data_source="docs.cloud.google.com", + content_length_bytes=1024, + content="# Creating Buckets...", + ): + self.name = name + self.title = title + self.uri = uri + self.data_source = data_source + self.content_length_bytes = content_length_bytes + self.content = content + + class Answer: + def __init__( + self, + answer_text=( + "Use `gcloud storage buckets create` to create a new storage" " bucket." + ), + citations=None, + references=None, + ): + self.answer_text = answer_text + self.citations = citations or [] + self.references = references or [] + + class SearchDocumentChunksResponse: + def __init__(self, results=None): + self.results = results or [DocumentChunk()] + + class BatchGetDocumentsResponse: + def __init__(self, documents=None): + self.documents = documents or [] + + class AnswerQueryResponse: + def __init__(self, answer=None): + self.answer = answer or Answer() + + class DeveloperKnowledgeClient: + def search_document_chunks(self, request=None): + return SearchDocumentChunksResponse() + + def get_document(self, request=None): + name = ( + request.name + if request and request.name + else "documents/docs.cloud.google.com/storage/docs/creating-buckets" + ) + return Document(name=name) + + def batch_get_documents(self, request=None): + names = request.names if request and request.names else [] + docs = [Document(name=n, title=f"Doc {n}") for n in names] + return BatchGetDocumentsResponse(documents=docs) + + def answer_query(self, request=None): + return AnswerQueryResponse() + + mock_dk.DeveloperKnowledgeClient = DeveloperKnowledgeClient + mock_dk.SearchDocumentChunksRequest = SearchDocumentChunksRequest + mock_dk.GetDocumentRequest = GetDocumentRequest + mock_dk.BatchGetDocumentsRequest = BatchGetDocumentsRequest + mock_dk.AnswerQueryRequest = AnswerQueryRequest + mock_dk.SearchDocumentChunksResponse = SearchDocumentChunksResponse + mock_dk.BatchGetDocumentsResponse = BatchGetDocumentsResponse + mock_dk.AnswerQueryResponse = AnswerQueryResponse + mock_dk.Document = Document + mock_dk.DocumentChunk = DocumentChunk + + mock_services = MagicMock() + mock_dk_service = MagicMock() + mock_pagers = MagicMock() + mock_pagers.SearchDocumentChunksPager = SearchDocumentChunksResponse + mock_dk_service.pagers = mock_pagers + mock_services.developer_knowledge = mock_dk_service + mock_dk.services = mock_services + + mock_google = MagicMock() + mock_cloud = MagicMock() + mock_cloud.developer_knowledge_v1 = mock_dk + mock_google.cloud = mock_cloud + + sys.modules["google"] = mock_google + sys.modules["google.cloud"] = mock_cloud + sys.modules["google.cloud.developer_knowledge_v1"] = mock_dk + sys.modules["google.cloud.developer_knowledge_v1.services"] = mock_services + sys.modules["google.cloud.developer_knowledge_v1.services.developer_knowledge"] = ( + mock_dk_service + ) + sys.modules[ + "google.cloud.developer_knowledge_v1.services.developer_knowledge.pagers" + ] = mock_pagers diff --git a/developer-knowledge/get_document.py b/developer-knowledge/get_document.py new file mode 100644 index 0000000000..398ac114f0 --- /dev/null +++ b/developer-knowledge/get_document.py @@ -0,0 +1,51 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START developerknowledge_get_document] +from google.cloud import developer_knowledge_v1 + + +def get_document( + name: str = "documents/docs.cloud.google.com/storage/docs/creating-buckets", +) -> developer_knowledge_v1.Document: + """Retrieves a single developer documentation page by its resource name. + + Args: + name: The resource name of the document in format + 'documents/{uri_without_scheme}'. + + Returns: + The Document containing the full Markdown content and metadata. + """ + client = developer_knowledge_v1.DeveloperKnowledgeClient() + + request = developer_knowledge_v1.GetDocumentRequest( + name=name, + ) + + document = client.get_document(request=request) + + print(f"Title: {document.title}") + print(f"URI: {document.uri}") + print(f"Data Source: {document.data_source}") + print(f"Content Length: {document.content_length_bytes} bytes") + print(f"Content Preview: {document.content[:150]}...\n") + + return document + + +# [END developerknowledge_get_document] + +if __name__ == "__main__": + get_document() diff --git a/developer-knowledge/get_document_test.py b/developer-knowledge/get_document_test.py new file mode 100644 index 0000000000..af499ff81c --- /dev/null +++ b/developer-knowledge/get_document_test.py @@ -0,0 +1,27 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import get_document + + +def test_get_document(capsys): + name = "documents/docs.cloud.google.com/storage/docs/creating-buckets" + doc = get_document.get_document(name=name) + out, _ = capsys.readouterr() + + assert doc is not None + assert doc.name == name + assert len(doc.title) > 0 + assert len(doc.content) > 0 + assert "Title:" in out diff --git a/developer-knowledge/requirements-test.txt b/developer-knowledge/requirements-test.txt new file mode 100644 index 0000000000..0adf0e81fc --- /dev/null +++ b/developer-knowledge/requirements-test.txt @@ -0,0 +1,3 @@ +google-developer-knowledge>=0.1.0 +pytest>=8.0.0 +pytest-cov>=5.0.0 diff --git a/developer-knowledge/requirements.txt b/developer-knowledge/requirements.txt new file mode 100644 index 0000000000..cafbb495a8 --- /dev/null +++ b/developer-knowledge/requirements.txt @@ -0,0 +1,2 @@ +google-developer-knowledge>=0.1.0 +pytest>=8.0.0 diff --git a/developer-knowledge/search_document_chunks.py b/developer-knowledge/search_document_chunks.py new file mode 100644 index 0000000000..68f695a88c --- /dev/null +++ b/developer-knowledge/search_document_chunks.py @@ -0,0 +1,58 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START developerknowledge_search_document_chunks] +from google.cloud import developer_knowledge_v1 + + +def search_document_chunks( + query: str = "How to create a Cloud Storage bucket", + page_size: int = 5, +) -> ( + developer_knowledge_v1.services.developer_knowledge.pagers.SearchDocumentChunksPager +): + """Searches developer documentation chunks for a given query. + + Args: + query: The natural language search query. + page_size: The maximum number of document chunks to return. + + Returns: + The SearchDocumentChunksPager containing relevant document chunks. + """ + client = developer_knowledge_v1.DeveloperKnowledgeClient() + + request = developer_knowledge_v1.SearchDocumentChunksRequest( + query=query, + page_size=page_size, + ) + + response = client.search_document_chunks(request=request) + + count = 0 + for chunk in response: + print(f"Parent Document: {chunk.parent}") + print(f"Chunk ID: {chunk.id}") + print(f"Content: {chunk.content[:100]}...\n") + count += 1 + if page_size > 0 and count >= page_size: + break + + return response + + +# [END developerknowledge_search_document_chunks] + +if __name__ == "__main__": + search_document_chunks() diff --git a/developer-knowledge/search_document_chunks_test.py b/developer-knowledge/search_document_chunks_test.py new file mode 100644 index 0000000000..25dc0940b0 --- /dev/null +++ b/developer-knowledge/search_document_chunks_test.py @@ -0,0 +1,29 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import search_document_chunks + + +def test_search_document_chunks(capsys): + response = search_document_chunks.search_document_chunks( + query="Cloud Storage bucket creation", + page_size=3, + ) + out, _ = capsys.readouterr() + + assert response is not None + assert len(response.results) > 0 + assert response.results[0].parent.startswith("documents/") + assert len(response.results[0].content) > 0 + assert "Parent Document: documents/" in out