This document provides information for developers who want to contribute to or modify the Congress.gov API Chatbot application.
- Development Environment Setup
- Project Structure
- Development Workflow
- Code Style and Standards
- Adding New Features
- Testing
- Debugging
- Performance Optimization
- Go 1.18+ installed
- Git
- Access to Congress.gov API (get your API key at https://api.congress.gov/sign-up/)
- Access to a GenAI LLM service (for development, you can use OpenAI or another compatible service)
- Clone the repository:
git clone https://github.com/cf-toolsuite/tanzu-genai-showcase.git
cd tanzu-genai-showcase/go-fiber-langchaingo- Create a
.envfile for local development:
cp .env.example .env- Edit the
.envfile to include your API keys:
CONGRESS_API_KEY=your_congress_api_key
GENAI_API_KEY=your_GENAI_API_KEY
GENAI_API_BASE_URL=your_GENAI_API_BASE_URL
LLM=gpt-4o-mini
- Install dependencies:
go mod tidy- Run the application:
go run cmd/server/main.goThe application will be available at http://localhost:8080
The project follows a clean architecture approach with the following structure:
go-fiber-langchaingo/
├── api/ # API clients (Congress.gov)
│ └── congress_client.go
├── cmd/ # Application entry points
│ └── server/
│ └── main.go
├── config/ # Configuration handling
│ └── config.go
├── docs/ # Documentation
│ ├── ARCHITECTURE.md
│ ├── API.md
│ ├── DEVELOPMENT.md
│ ├── FEATURES.md
│ ├── IMPLEMENTATION.md
│ └── LOGGING.md
├── internal/ # Private application code
│ ├── handler/
│ │ └── handler.go
│ └── service/
│ ├── chatbot.go
│ └── chatbot_tools.go
├── pkg/ # Public libraries
│ ├── llm/
│ │ └── llm.go
│ └── logger/
│ └── logger.go
├── logs/ # Log files
├── public/ # Static files
│ └── index.html
├── .env.example # Example environment variables
├── go.mod # Go module definition
├── go.sum # Go module checksums
├── Makefile # Build and development commands
├── manifest.yml # Cloud Foundry manifest
└── README.md # Project overview
- Create a new branch for your changes:
git checkout -b feature/your-feature-name-
Make your changes to the codebase.
-
Run the application locally to test your changes:
go run cmd/server/main.go- Build the application to ensure it compiles correctly:
go build -o congress-chatbot cmd/server/main.go- Commit your changes:
git add .
git commit -m "Add your feature description"- Push your changes to the remote repository:
git push origin feature/your-feature-name- Create a pull request for your changes.
The project includes a Makefile with common development tasks:
make build: Build the applicationmake run: Run the application locallymake test: Run testsmake lint: Run lintersmake clean: Clean build artifacts
Example:
make build
make run- Follow the Go Code Review Comments for style guidance.
- Use
gofmtorgoimportsto format your code. - Follow the Effective Go guidelines.
- Use camelCase for variable and function names.
- Use PascalCase for exported functions, types, and variables.
- Use snake_case for file names.
- Always check errors and return them to the caller.
- Use descriptive error messages.
- Wrap errors with context using
fmt.Errorf("failed to do something: %w", err).
- Add comments to exported functions, types, and variables.
- Use complete sentences with proper punctuation.
- Explain the "why" rather than the "what" when possible.
- Add a new method to the
CongressClientinapi/congress_client.go:
// SearchNewEndpoint searches for new data in the Congress.gov API
func (c *CongressClient) SearchNewEndpoint(query string, offset, limit int) (map[string]interface{}, error) {
endpoint := fmt.Sprintf("%s/new-endpoint", c.baseURL)
params := url.Values{}
params.Add("api_key", c.apiKey)
if query != "" {
params.Add("query", query)
}
params.Add("offset", fmt.Sprintf("%d", offset))
params.Add("limit", fmt.Sprintf("%d", limit))
return c.makeRequest(endpoint, params)
}- Add a new case to the
executeCongressToolmethod ininternal/service/chatbot_tools.go:
case "search_new_endpoint":
var params struct {
Query string `json:"query"`
}
if err := json.Unmarshal([]byte(args), ¶ms); err != nil {
return "", fmt.Errorf("failed to parse search_new_endpoint args: %w", err)
}
result, err = s.congressClient.SearchNewEndpoint(params.Query, 0, 5)- Add a new tool to the
createCongressToolsmethod ininternal/service/chatbot_tools.go:
searchNewEndpointTool := llms.Tool{
Type: "function",
Function: &llms.FunctionDefinition{
Name: "search_new_endpoint",
Description: "Search for new data in the Congress.gov API. Use this when the user asks about new data.",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"query": map[string]any{
"type": "string",
"description": "Search query for new data",
},
},
"required": []string{"query"},
},
},
}- Add the new tool to the list of tools returned by
createCongressTools:
return []llms.Tool{
// Existing tools...
searchNewEndpointTool,
}- Update the system prompt in
Initializemethod ininternal/service/chatbot.goto include the new capability.
- Add a new handler method to the
Handlerstruct ininternal/handler/handler.go:
// HandleNewEndpoint handles requests to the new endpoint
func (h *Handler) HandleNewEndpoint(c *fiber.Ctx) error {
// Implementation
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"status": "ok",
})
}- Register the new endpoint in the
RegisterRoutesmethod ininternal/handler/handler.go:
api.Get("/new-endpoint", h.HandleNewEndpoint)Run all tests:
go test ./...Run tests for a specific package:
go test ./internal/serviceRun tests with coverage:
go test -cover ./...- Place test files in the same package as the code they test.
- Name test files with a
_test.gosuffix. - Name test functions with a
Testprefix followed by the name of the function being tested.
Example:
// chatbot_test.go
package service
import (
"context"
"testing"
)
func TestProcessUserQuery(t *testing.T) {
// Test implementation
}The application uses a comprehensive logging system that writes logs to both the console and a log file (logs/http.log). You can use the logger to add debug information:
import "github.com/cf-toolsuite/tanzu-genai-showcase/go-fiber-langchaingo/pkg/logger"
// Log an informational message
logger.InfoLogger.Printf("Processing request: %s", requestID)
// Log an error
logger.ErrorLogger.Printf("Failed to process request: %v", err)You can use the curl command to debug HTTP requests:
# Test the chat endpoint
curl -X POST http://localhost:8080/api/chat \
-H "Content-Type: application/json" \
-d '{"message": "Who are the current senators from Washington state?"}'
# Test the health endpoint
curl -X GET http://localhost:8080/api/healthThe application implements a caching mechanism to improve performance and reduce the number of API calls to the Congress.gov API. The cache is implemented in the CongressClient struct in api/congress_client.go.
To modify the cache behavior:
- Adjust the cache TTL (time-to-live) in the
makeRequestmethod:
// Cache the response for 10 minutes
c.cache.Set(cacheKey, result, 10*time.Minute)- Add cache invalidation for specific endpoints if needed:
// Invalidate cache for a specific key
c.cache.mutex.Lock()
delete(c.cache.data, cacheKey)
c.cache.mutex.Unlock()LLM calls can be expensive and slow. To optimize LLM usage:
- Adjust the temperature parameter in the LLM client to balance creativity and consistency:
opts := []llms.CallOption{
llms.WithModel(model),
llms.WithTemperature(0.3), // Lower for more consistent responses
llms.WithMaxTokens(8192),
}- Use more specific prompts to get better responses:
systemPrompt := `
You are a helpful assistant that provides information about the U.S. Congress.
Be precise and factual, focusing on providing accurate and current information.
`- Implement streaming responses for a more interactive experience:
// TODO: Implement streaming responses