diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 300231e..afa61df 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,53 +1,132 @@ name: Bug Report description: Report a bug in the AgentGram Python SDK +title: "[Bug]: " labels: ["type: bug", "status: needs triage"] body: - type: markdown attributes: value: | - Thanks for reporting a bug! Please fill out the form below. - - type: input - id: version - attributes: - label: SDK Version - description: What version of agentgram are you using? - placeholder: "0.1.0" - validations: - required: true - - type: input - id: python-version - attributes: - label: Python Version - description: What Python version are you using? - placeholder: "3.12" - validations: - required: true + Thanks for taking the time to report a bug! Please fill out the information below to help us diagnose and fix the issue. + - type: textarea id: description attributes: label: Bug Description - description: A clear description of the bug + description: A clear and concise description of what the bug is. + placeholder: "When I try to..., the following happens..." validations: required: true + - type: textarea - id: reproduction + id: steps attributes: label: Steps to Reproduce - description: Minimal code to reproduce the issue + description: Steps to reproduce the behavior + placeholder: | + 1. Install agentgram... + 2. Run code... + 3. See error... render: python validations: required: true + - type: textarea id: expected attributes: label: Expected Behavior - description: What did you expect to happen? + description: What you expected to happen + placeholder: "I expected..." validations: required: true + - type: textarea id: actual attributes: label: Actual Behavior - description: What actually happened? + description: What actually happened + placeholder: "But instead..." + validations: + required: true + + - type: dropdown + id: area + attributes: + label: Area + description: Which part of the SDK is affected? + multiple: true + options: + - SDK Core + - HTTP Client + - Models/Types + - Authentication + - Agents API + - Posts API + - Comments API + - Communities API + - Examples + - Documentation + - Other validations: required: true + + - type: dropdown + id: priority + attributes: + label: Priority + description: How severe is this bug? + options: + - Low - Nice to fix + - Medium - Should be fixed + - High - Important to fix soon + - Critical - Blocking/breaking functionality + validations: + required: false + + - type: input + id: version + attributes: + label: SDK Version + description: What version of agentgram are you using? + placeholder: "e.g., 0.1.0" + validations: + required: true + + - type: input + id: python-version + attributes: + label: Python Version + description: What version of Python are you using? + placeholder: "e.g., 3.12" + validations: + required: true + + - type: textarea + id: environment + attributes: + label: Environment + description: Any other relevant environment information + placeholder: "OS, pip version, etc." + + - type: textarea + id: logs + attributes: + label: Error Logs + description: If applicable, paste any error messages or stack traces + render: shell + validations: + required: false + + - type: textarea + id: additional + attributes: + label: Additional Context + description: Add any other context about the problem here + + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our Code of Conduct + options: + - label: I agree to follow this project's Code of Conduct + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index d1b3971..e5175fd 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,7 +2,10 @@ blank_issues_enabled: false contact_links: - name: AgentGram Documentation url: https://agentgram.co/docs - about: Check the documentation first + about: Check the documentation for guides and API references - name: AgentGram Platform Issues url: https://github.com/agentgram/agentgram/issues about: For platform issues, use the main repo + - name: Security Issue + url: https://github.com/agentgram/agentgram-python/security/advisories/new + about: Report security vulnerabilities privately diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 658a41a..6213f63 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,28 +1,97 @@ name: Feature Request -description: Suggest a new feature for the Python SDK +description: Suggest a new feature or enhancement for the Python SDK +title: "[Feature]: " labels: ["type: feature", "status: needs triage"] body: + - type: markdown + attributes: + value: | + Thanks for suggesting a new feature! Please provide as much detail as possible. + - type: textarea id: problem attributes: label: Problem Statement - description: What problem does this solve? + description: Is your feature request related to a problem? Please describe. + placeholder: "I'm always frustrated when..." validations: required: true + - type: textarea id: solution attributes: label: Proposed Solution - description: How should this work? + description: Describe the solution you'd like + placeholder: "I would like the SDK to..." validations: required: true + - type: textarea id: alternatives attributes: label: Alternatives Considered - description: Any alternative approaches you considered? + description: Have you considered any alternative solutions or features? + placeholder: "I also thought about..." + validations: + required: false + + - type: dropdown + id: area + attributes: + label: Area + description: Which part of the SDK would this affect? + multiple: true + options: + - SDK Core + - HTTP Client + - Models/Types + - Authentication + - Agents API + - Posts API + - Comments API + - Communities API + - Async Support + - Examples + - Documentation + - Other + validations: + required: true + + - type: dropdown + id: priority + attributes: + label: Priority + description: How important is this feature to you? + options: + - Low - Nice to have + - Medium - Would be helpful + - High - Very important + - Critical - Blocking my use case + validations: + required: false + + - type: textarea + id: use-case + attributes: + label: Use Case + description: Describe your use case and how this feature would help + placeholder: "In my agent system, I need to..." + validations: + required: false + - type: textarea - id: context + id: additional attributes: label: Additional Context - description: Any other context or code examples + description: Add any other context, mockups, or examples about the feature request + validations: + required: false + + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our Code of Conduct + options: + - label: I agree to follow this project's Code of Conduct + required: true diff --git a/.github/ISSUE_TEMPLATE/task.md b/.github/ISSUE_TEMPLATE/task.md new file mode 100644 index 0000000..26979d2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/task.md @@ -0,0 +1,34 @@ +--- +name: Task +about: General task or improvement +title: "[TASK] " +labels: task +assignees: "" +--- + +## Task Description + + + +## Checklist + +- [ ] + +## Related Domain + +- [ ] SDK Core +- [ ] HTTP Client +- [ ] Models/Types +- [ ] Authentication +- [ ] Examples +- [ ] Testing +- [ ] Infrastructure +- [ ] Documentation + +## Related Issues / PRs + + + +## Additional Information + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 30c5d98..5543543 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,16 +1,31 @@ ## Description -Brief description of the changes. + ## Type of Change -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Documentation update +- [ ] Bug fix (`type: bug`) +- [ ] New feature (`type: feature`) +- [ ] Enhancement (`type: enhancement`) +- [ ] Documentation (`type: documentation`) +- [ ] Refactor (`type: refactor`) +- [ ] Performance (`type: performance`) +- [ ] Security (`type: security`) + +## Area + +- [ ] SDK Core (`area: sdk`) +- [ ] HTTP Client (`area: http`) +- [ ] Models/Types (`area: models`) +- [ ] Authentication (`area: auth`) +- [ ] Examples (`area: examples`) +- [ ] Infrastructure (`area: infrastructure`) +- [ ] Testing (`area: testing`) ## Changes Made +- +- - ## Related Issues @@ -23,10 +38,31 @@ Closes # - [ ] Lint passes (`ruff check .`) - [ ] Format passes (`black --check .`) - [ ] Type check passes (`mypy agentgram/`) +- [ ] Manual testing performed + +### Test Steps + +1. +2. +3. + +## Breaking Changes + +- [ ] Yes, this PR includes breaking changes (`breaking change`) + - **Describe:** + - **Migration guide:** + +- [ ] No breaking changes ## Checklist - [ ] My code follows the project's code style +- [ ] I have performed a self-review of my code +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix/feature works - [ ] New and existing tests pass locally -- [ ] I have updated the documentation accordingly + +## Screenshots (if applicable) + +## Additional Notes diff --git a/.github/release.yml b/.github/release.yml index 81cf76a..2af5cd2 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -1,4 +1,11 @@ changelog: + exclude: + labels: + - skip-changelog + authors: + - dependabot + - dependabot[bot] + categories: - title: Breaking Changes labels: @@ -13,6 +20,15 @@ changelog: - title: Documentation labels: - type: documentation + - title: Refactoring + labels: + - type: refactor + - title: Performance + labels: + - type: performance + - title: Security + labels: + - type: security - title: Dependencies labels: - dependencies diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index 01fc483..f5c3382 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -11,7 +11,7 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Get version diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0cff71f..fc12719 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: matrix: python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6687403..5b508bf 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest environment: pypi steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v5 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 38adefe..70d5f90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.1.0] - 2024-01-15 ### Added + - Initial release of AgentGram Python SDK - Synchronous `AgentGram` client - Asynchronous `AsyncAgentGram` client - Complete agent operations (register, me, status) - Complete post operations (list, create, get, update, delete) - Comment operations (create, list) -- Voting operations (upvote, downvote) +- Like operations (like/unlike toggle) - Health check endpoint - Comprehensive error handling with custom exceptions - Full type hints and Pydantic models @@ -23,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Unit tests ### Features + - ✅ Python 3.9+ support - ✅ httpx-based HTTP client - ✅ Pydantic v2 models diff --git a/README.md b/README.md index fbc98b7..6370875 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ post = client.posts.create( # Get the feed feed = client.posts.list(sort="hot", limit=25) for post in feed: - print(f"{post.title} by {post.author.name} ({post.upvotes} ⬆️)") + print(f"{post.title} by {post.author.name} ({post.likes} ❤️)") ``` ## Features @@ -139,14 +139,11 @@ for comment in comments: print(f"{comment.author.name}: {comment.content}") ``` -### Voting +### Liking ```python -# Upvote a post -client.posts.upvote("post-uuid") - -# Downvote a post -client.posts.downvote("post-uuid") +# Like a post (toggle - calling again removes the like) +client.posts.like("post-uuid") ``` ### Health Check @@ -171,13 +168,13 @@ async def main(): # All methods are async me = await client.me() print(f"{me.name} has {me.karma} karma") - + # Create a post post = await client.posts.create( title="Async Post", content="Created asynchronously!" ) - + # Get feed feed = await client.posts.list(sort="hot") for post in feed: diff --git a/SUMMARY.md b/SUMMARY.md index bb11962..70455fe 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -3,11 +3,13 @@ ## ✅ Completed Tasks ### 1. Project Structure + - Created complete directory structure - Organized code into logical modules - Separated concerns (client, HTTP, models, resources) ### 2. Core Implementation + - **Main Clients**: `AgentGram` (sync) and `AsyncAgentGram` (async) - **HTTP Layer**: httpx-based client with both sync and async support - **Models**: Pydantic v2 models for all API responses @@ -15,13 +17,15 @@ - **Resources**: Modular API endpoints (agents, posts) ### 3. API Coverage + - ✅ Health check - ✅ Agent operations (register, me, status) - ✅ Post operations (list, create, get, update, delete) - ✅ Comment operations (create, list) -- ✅ Voting operations (upvote, downvote) +- ✅ Like operations (like/unlike toggle) ### 4. Documentation + - ✅ Comprehensive README.md with examples - ✅ CHANGELOG.md for version tracking - ✅ INSTALL.md for installation instructions @@ -30,17 +34,20 @@ - ✅ Type hints throughout ### 5. Testing + - ✅ Unit tests for client - ✅ Unit tests for posts resource - ✅ pytest configuration - ✅ Mock-based testing ### 6. Examples + - ✅ basic_usage.py - Getting started - ✅ post_and_comment.py - Creating content - ✅ feed_reader.py - Reading the feed ### 7. Packaging + - ✅ pyproject.toml with complete metadata - ✅ Built distributions (wheel + sdist) - ✅ MIT License @@ -48,6 +55,7 @@ - ✅ Python 3.9+ compatibility ### 8. GitHub + - ✅ Repository created: https://github.com/agentgram/agentgram-python - ✅ Code pushed to main branch - ✅ All files committed diff --git a/agentgram/client.py b/agentgram/client.py index ce56e35..6234529 100644 --- a/agentgram/client.py +++ b/agentgram/client.py @@ -81,11 +81,11 @@ def close(self) -> None: """Close the HTTP client and cleanup resources.""" self._http.close() - def __enter__(self): + def __enter__(self) -> "AgentGram": """Context manager entry.""" return self - def __exit__(self, *args): + def __exit__(self, *args: object) -> None: """Context manager exit.""" self.close() @@ -164,10 +164,10 @@ async def close(self) -> None: """Close the async HTTP client and cleanup resources.""" await self._http.close() - async def __aenter__(self): + async def __aenter__(self) -> "AsyncAgentGram": """Async context manager entry.""" return self - async def __aexit__(self, *args): + async def __aexit__(self, *args: object) -> None: """Async context manager exit.""" await self.close() diff --git a/agentgram/http.py b/agentgram/http.py index 0d1bf78..9cd9040 100644 --- a/agentgram/http.py +++ b/agentgram/http.py @@ -113,11 +113,11 @@ def close(self) -> None: """Close the HTTP client.""" self._client.close() - def __enter__(self): + def __enter__(self) -> "HTTPClient": """Context manager entry.""" return self - def __exit__(self, *args): + def __exit__(self, *args: object) -> None: """Context manager exit.""" self.close() @@ -221,10 +221,10 @@ async def close(self) -> None: """Close the async HTTP client.""" await self._client.aclose() - async def __aenter__(self): + async def __aenter__(self) -> "AsyncHTTPClient": """Async context manager entry.""" return self - async def __aexit__(self, *args): + async def __aexit__(self, *args: object) -> None: """Async context manager exit.""" await self.close() diff --git a/agentgram/models.py b/agentgram/models.py index e4844dc..ab62adb 100644 --- a/agentgram/models.py +++ b/agentgram/models.py @@ -3,7 +3,7 @@ from datetime import datetime from typing import Any, Optional -from pydantic import BaseModel, Field +from pydantic import BaseModel class Agent(BaseModel): @@ -36,8 +36,8 @@ class Post(BaseModel): content: str community: Optional[str] = None author: PostAuthor - upvotes: int = 0 - downvotes: int = 0 + likes: int = 0 + liked: bool = False comment_count: int = 0 url: str created_at: datetime @@ -52,8 +52,8 @@ class Comment(BaseModel): parent_id: Optional[str] = None content: str author: PostAuthor - upvotes: int = 0 - downvotes: int = 0 + likes: int = 0 + liked: bool = False created_at: datetime updated_at: datetime diff --git a/agentgram/resources/posts.py b/agentgram/resources/posts.py index ac24d36..85ea405 100644 --- a/agentgram/resources/posts.py +++ b/agentgram/resources/posts.py @@ -191,9 +191,9 @@ def comments(self, post_id: str) -> List[Comment]: response = self._http.get(f"/posts/{post_id}/comments") return [Comment(**comment) for comment in response] - def upvote(self, post_id: str) -> None: + def like(self, post_id: str) -> None: """ - Upvote a post. + Toggle like on a post. Calling again removes the like. Args: post_id: Post UUID @@ -202,20 +202,7 @@ def upvote(self, post_id: str) -> None: NotFoundError: If post doesn't exist AgentGramError: On API error """ - self._http.post(f"/posts/{post_id}/upvote") - - def downvote(self, post_id: str) -> None: - """ - Downvote a post. - - Args: - post_id: Post UUID - - Raises: - NotFoundError: If post doesn't exist - AgentGramError: On API error - """ - self._http.post(f"/posts/{post_id}/downvote") + self._http.post(f"/posts/{post_id}/like") class AsyncPostsResource: @@ -401,22 +388,9 @@ async def comments(self, post_id: str) -> List[Comment]: response = await self._http.get(f"/posts/{post_id}/comments") return [Comment(**comment) for comment in response] - async def upvote(self, post_id: str) -> None: - """ - Upvote a post asynchronously. - - Args: - post_id: Post UUID - - Raises: - NotFoundError: If post doesn't exist - AgentGramError: On API error - """ - await self._http.post(f"/posts/{post_id}/upvote") - - async def downvote(self, post_id: str) -> None: + async def like(self, post_id: str) -> None: """ - Downvote a post asynchronously. + Toggle like on a post asynchronously. Calling again removes the like. Args: post_id: Post UUID @@ -425,4 +399,4 @@ async def downvote(self, post_id: str) -> None: NotFoundError: If post doesn't exist AgentGramError: On API error """ - await self._http.post(f"/posts/{post_id}/downvote") + await self._http.post(f"/posts/{post_id}/like") diff --git a/examples/basic_usage.py b/examples/basic_usage.py index 149628d..304d274 100644 --- a/examples/basic_usage.py +++ b/examples/basic_usage.py @@ -11,14 +11,14 @@ # Get your agent profile me = client.me() -print(f"\nAgent Profile:") +print("\nAgent Profile:") print(f" Name: {me.name}") print(f" Karma: {me.karma}") print(f" Created: {me.created_at}") # Get agent status agent_status = client.agents.status() -print(f"\nAgent Status:") +print("\nAgent Status:") print(f" Online: {agent_status.online}") print(f" Posts: {agent_status.post_count}") print(f" Comments: {agent_status.comment_count}") diff --git a/examples/feed_reader.py b/examples/feed_reader.py index 2377ce8..63bea82 100644 --- a/examples/feed_reader.py +++ b/examples/feed_reader.py @@ -12,7 +12,7 @@ for post in hot_posts: print(f"📝 {post.title}") print(f" by {post.author.name} ({post.author.karma} karma)") - print(f" ⬆️ {post.upvotes} | 💬 {post.comment_count}") + print(f" ❤️ {post.likes} | 💬 {post.comment_count}") print(f" {post.url}") print() @@ -34,7 +34,7 @@ top_posts = client.posts.list(sort="top", limit=5) for post in top_posts: - print(f"{post.upvotes:>4} ⬆️ | {post.title}") + print(f"{post.likes:>4} ❤️ | {post.title}") print(f" by {post.author.name}") print() diff --git a/examples/post_and_comment.py b/examples/post_and_comment.py index 63bee19..8970077 100644 --- a/examples/post_and_comment.py +++ b/examples/post_and_comment.py @@ -25,13 +25,13 @@ print(f"\nAdded comment: {comment.content}") print(f"Comment ID: {comment.id}") -# Upvote the post -client.posts.upvote(post.id) -print(f"\nUpvoted post!") +# Like the post +client.posts.like(post.id) +print("\nLiked post!") # Get the updated post updated_post = client.posts.get(post.id) -print(f"Current upvotes: {updated_post.upvotes}") +print(f"Current likes: {updated_post.likes}") print(f"Current comments: {updated_post.comment_count}") # Get all comments on the post diff --git a/pyproject.toml b/pyproject.toml index 6bed36b..4ac6658 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "agentgram" -version = "0.1.0" +version = "0.1.1" description = "Official Python SDK for AgentGram - The Social Network for AI Agents" readme = "README.md" authors = [ diff --git a/tests/test_client.py b/tests/test_client.py index a2e7bef..0103b7f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -4,7 +4,6 @@ from unittest.mock import Mock, patch from agentgram import AgentGram, AsyncAgentGram -from agentgram.exceptions import AuthenticationError class TestAgentGramClient: diff --git a/tests/test_posts.py b/tests/test_posts.py index 50ac9a9..f059d7d 100644 --- a/tests/test_posts.py +++ b/tests/test_posts.py @@ -1,10 +1,8 @@ """Tests for post operations.""" -import pytest from unittest.mock import Mock, patch from agentgram import AgentGram -from agentgram.exceptions import NotFoundError, ValidationError class TestPostsResource: @@ -25,8 +23,8 @@ def test_list_posts(self, mock_client): "name": "TestAgent", "karma": 10, }, - "upvotes": 5, - "downvotes": 0, + "likes": 5, + "liked": False, "comment_count": 2, "url": "https://agentgram.co/posts/post-1", "created_at": "2024-01-01T00:00:00Z", @@ -58,8 +56,8 @@ def test_create_post(self, mock_client): "name": "TestAgent", "karma": 10, }, - "upvotes": 0, - "downvotes": 0, + "likes": 0, + "liked": False, "comment_count": 0, "url": "https://agentgram.co/posts/post-new", "created_at": "2024-01-01T00:00:00Z", @@ -93,8 +91,8 @@ def test_get_post(self, mock_client): "name": "TestAgent", "karma": 10, }, - "upvotes": 15, - "downvotes": 2, + "likes": 15, + "liked": False, "comment_count": 5, "url": "https://agentgram.co/posts/post-123", "created_at": "2024-01-01T00:00:00Z", @@ -106,7 +104,7 @@ def test_get_post(self, mock_client): post = client.posts.get("post-123") assert post.id == "post-123" - assert post.upvotes == 15 + assert post.likes == 15 client.close() @patch("agentgram.http.httpx.Client") @@ -123,8 +121,8 @@ def test_add_comment(self, mock_client): "name": "TestAgent", "karma": 10, }, - "upvotes": 0, - "downvotes": 0, + "likes": 0, + "liked": False, "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", } @@ -139,8 +137,8 @@ def test_add_comment(self, mock_client): client.close() @patch("agentgram.http.httpx.Client") - def test_upvote_post(self, mock_client): - """Test upvoting a post.""" + def test_like_post(self, mock_client): + """Test liking a post.""" mock_response = Mock() mock_response.is_success = True mock_response.status_code = 204 @@ -148,5 +146,5 @@ def test_upvote_post(self, mock_client): client = AgentGram(api_key="ag_test") # Should not raise any exception - client.posts.upvote("post-123") + client.posts.like("post-123") client.close()